SQL join for multiple columns with ids - mysql

I have two SQL tables. The first table stores a list of athletes with their id and name. For example:
athlete_id | first_name | last_name
-----------|------------|----------
1 | Matthew | Reese
2 | Tiffanie | Renz
3 | Tom | Dow
etc...
The second table stores entries for a track (sprint) event, and the id of the athlete competing in each lane. For example:
event_id | lane1_athlete_id | lane2_athlete_id | lane3_athlete_id
---------|------------------|------------------|-----------------
1 | 1 | 15 | 24
2 | 18 | 2 | 4
3 | 78 | 50 | 3
etc...
I need to create an SQL query which will return that second table, but with the athlete ids resolved to the athlete names. For example:
event_id | lane1_athlete | lane2_athlete | lane3_athlete
---------|---------------|---------------|--------------
1 | Matthew Reese | Lesa Allain | Nicole Spiers
2 | Emmy Bartol | Tiffanie Renz | Louise Baier
3 | Zack Bui | Norah Flagg | Tom Dow
I imagine this involves a table join, but I can't get my head around the correct query. Any help would be appreciated.

Join the second table to the first one, three times:
SELECT
e.event_id,
CONCAT(a1.first_name, ' ', a1.last_name) AS lane1_athlete,
CONCAT(a2.first_name, ' ', a2.last_name) AS lane2_athlete,
CONCAT(a3.first_name, ' ', a3.last_name) AS lane3_athlete
FROM events e
LEFT JOIN athletes a1
ON e.lane1_athlete_id = a1.athlete_id
LEFT JOIN athletes a2
ON e.lane2_athlete_id = a2.athlete_id
LEFT JOIN athletes a3
ON e.lane3_athlete_id = a3.athlete_id;

Related

MYSQL selecting new column based on multiple joins

I'm still working through some kinks with MySQL so any help will be appreciated.
I have 3 tables -- equipment, states, zones.
equipment:
+---------------+------+------------+
| current_state | id | ...columns |
+---------------+------+------------+
states:
+----------+-------------+
| state | zone_id |
+----------+-------------+
zones:
+-----+------+
| id | zone |
+-----+------+
In equipment, there is one current_state per row.
In states, there is one zone_id per row.
In zones, there is one zone per row.
I would like to JOIN the three tables as a subquery select statement (not even sure if that's a thing) and have the output return as 1 alias'd column among the other columns I'm selecting
+--------------+-------------+
| current_zone | ....columns |
+--------------+-------------+
A sample expected output is:
+------------+-------------+--------+------------------+--------------+---------+
| c_id | g_id | e_id | equipment_type | impressionId | email |
+------------+-------------+--------+------------------+--------------+---------+
| 1234 | ABC1234 | 0001 | VEST | 2032 |ab#yc.com|
| 1234 | 1234ABC | 0001 | SHIRT | 4372 |ab#yc.com|
| 1234 | DCBA123 | 0001 | CAN | 4372 |ab#yc.com|
| 1234 | DCBA321 | 0001 | JACKET | ab#yc.com |ab#yw.com|
| 4567 | abc321d | 0002 | SHIRT | 2032 |db#yw.com|
| 4567 | cba123d | 0002 | CAN | 4372 |db#yw.com|
| 4567 | def4rg4 | 0002 | JEANS | 3210 |db#yw.com|
+------------+-------------+--------+------------------+--------------+---------+
The current query has multiple joins already referring to the zones and states table in order to determine a different value:
SELECT equipment.*,
...
FROM equipment
LEFT JOIN c on equipment.c_id = c.id
LEFT JOIN g on equipment.g_id = g.id
LEFT JOIN states on g.state = states.state
LEFT JOIN zones on zones.id = states.zone_id
Essentially, what I want to do is create a subquery in order to create a new column based on the results of the three joins, something like this:
SELECT equipment.*,
(SELECT
equipment.current_state
FROM equipment
LEFT JOIN equipment.current_state = states.state
LEFT JOIN zones.id = states.zone_id
) as current_zone,
...
This is even possible? Am I trying to select a new column in the wrong place?
Thanks to #TheImpaler I was able to clear up my Scalar Subquery. In my eyes, I thought I had to create another join based on the properties I wanted when in reality all I had to do was create a conditional scalar subquery:
SELECT equipment.*,
(SELECT zones.zone
FROM zones
WHERE equipment.current_state = states.state
AND zones.id = states.zone_id
) as current_zone,
...

MySQL - join multiple mapped tables and count records with different mapping conditions

It's the 3rd day I'm trying to write a MySQL query. Did lots of search, but it still doesn't work as expected. I'll try to simplify tables as much as possible
System has tkr_restaurants table:
restaurant_id | restaurant_name
1 | AA
2 | BB
3 | CC
Each restaurant has a division assigned (tkr_divisions table):
division_id | restaurant_id | division_name
1 | 1 | AA-1
2 | 1 | AA-2
3 | 2 | BB-1
Then there are meals in tkr_meals_to_restaurants_divisions table, where each meal can be assigned (mapped) to whole restaurant(s) and/or specific division(s). If meal is mapped to restaurant, all restaurant's divisions should see it. If meal is mapped to division(s), only specific division(s) should see it.
meal_id | mapped_restaurant_id | mapped_division_id
1 | 1 | NULL
2 | NULL | 1
3 | NULL | 2
I need to display a list of restaurants and number of meals mapped to it depending on user permissions.
Example 1: if user has permissions to access whole restaurant_id 1 and restaurant_3 (and no specific divisions), then list should be:
AA | 3
CC | 0
(because user can access meals mapped to restaurant 1 + all its division, and restaurant 3 + all its divisions (even if restaurant 3 has no divisions/meals mapped))
Example 2: if user has permissions to access only division_id 1, then list should be:
AA | 1
(because user can only access meals mapped to division 1).
The closest query I could get is:
Example 1:
SELECT *,
(SELECT COUNT(DISTINCT meal_id)
FROM
tkr_meals_to_restaurants_divisions
WHERE
tkr_meals_to_restaurants_divisions.mapped_restaurant_id=tkr_restaurants.restaurant_id
OR tkr_meals_to_restaurants_divisions.mapped_division_id=tkr_divisions.division_id)AS total_meals
FROM
tkr_restaurants
LEFT JOIN
tkr_divisions
ON tkr_restaurants.restaurant_id=tkr_divisions.restaurant_id
WHERE
tkr_restaurants.restaurant_id IN (1, 3)
OR tkr_restaurants.restaurant_id IN (
SELECT restaurant_id
FROM tkr_divisions
WHERE division_id IN (NULL)
)
GROUP BY
tkr_restaurants.restaurant_id
ORDER BY
tkr_restaurants.restaurant_name
However, result was:
AA | 2
CC | 0
I believe I'm greatly over-complicating this query, but all the simpler queries I wrote produced even more inaccurate results.
What about this query:
SELECT
FROM tkr_restaurants AS a
JOIN tkr_divisions AS b
ON a.restaurant_id = b.restaurant_id
LEFT OUTER JOIN tkr_meals_to_restaurants_divisions AS c
ON (c.mapped_restaurant_id = a.restaurant_id OR c.mapped_division_id = b.division_id)
As a Base four your further work. It combine all information into one table. If you add e.g. this:
WHERE a.restaurant_id IN (1, 3)
the result will be
| restaurant_id | restaurant_name | division_id | restaurant_id | division_name | meal_id | mapped_restaurant_id | mapped_division_id |
|---------------|-----------------|-------------|---------------|---------------|---------|----------------------|--------------------|
| 1 | AA | 1 | 1 | AA-1 | 1 | 1 | (null) |
| 1 | AA | 2 | 1 | AA-2 | 1 | 1 | (null) |
| 1 | AA | 1 | 1 | AA-1 | 2 | (null) | 1 |
| 1 | AA | 2 | 1 | AA-2 | 3 | (null) | 2 |
just count the distinct meal ids with COUNT(DISTINCT c.meal_id) and take the restaurant name to get AA: 3 for your example 2
I used a sqlfiddle: http://sqlfiddle.com/#!9/fa2b78/18/0
[EDIT]
Change JOIN tkr_divisions AS b to LEFT OUTER JOIN tkr_divisions AS b
Change SELECT * to SELECT a.restaurant_name, COUNT(DISTINCT c.meal_id)
Add a GROUP BY a.restaurant_name at the end.
Update the SQL Fiddle (new link)

mySQL JOIN of several tables

I do have the following three tables in a MySQL-DB (InnoDB)
UserTab
ID | Name | ---
------------------
1 | Tom |
2 | Dick |
3 | Harry |
EventTab
ID | Name | ---
------------------
1 | Easter |
2 | Holidays |
3 | ThxGiving |
4 | Christmas |
ParticipationTab
ID | UserID | EventID
---------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 3 | 3
And I want to achieve the follwing result with my query:
QueryResultTab
UserTab.Name | EventTab.Name | NoPart | Names
-----------------------------------------------
Tom | Easter | 2 | Tom, Dick
Tom | Holidays | 1 | Tom
Tom | ThxGiving | 2 | Tom, Harry
Dick | Easter | 2 | Tom, Dick
Dick | Christmas | 1 | Dick
Harry | ThxGiving | 2 | Tom, Harry
I do know about Count() combined with GROUP to get the number of participants
I know about group-concat to get the "Names".
SELECT Event, GROUP_CONCAT(Name ORDER BY Name ASC SEPARATOR ', ') as Names
FROM
(SELECT ID as UserID, Name FROM X_Users WHERE ConditionA) AS UserTab
INNER JOIN
(SELECT EventID, UserID FROM X_Participation WHERE ConditionB) AS ParticipationTab
ON UserTab.UserID = ParticipationTab.UserID
INNER JOIN
(SELECT ID as EventID, Event FROM X_Events WHERE ConditionC) AS EventTab
ON ParticipationTab.EventID = EventTab.EventID
GROUP BY EventTab.EventID
This gives me:
ConcatTab
EventTab.Name | Names
---------------------------
Easter | Tom, Dick
Holidays | Tom
ThxGiving | Tom, Harry
Easter | Tom, Dick
Christmas | Dick
ThxGiving | Tom, Harry
I know about JOINs as you can see. Probably I could use LEFT or RIGHT JOINs as well for this.
For the other parts I use this query:
SELECT Name, Event, NoPart
FROM (SELECT ID as UserID, Name FROM X_Users WHERE ConditionA) AS UserTab
INNER JOIN (SELECT EventID, UserID FROM X_Participation WHERE ConditionB) AS PartTab
ON UserTab.UserID = PartTab.UserID
INNER JOIN (SELECT ID as EventID, Event FROM X_Events WHERE ConditionC) AS EvTab
ON PartTab.EventID = EvTab.EventID
INNER JOIN (SELECT EventID as CntID, COUNT(*) AS NoPart FROM X_Participation WHERE ConditionB) AS CntTab
ON EvTab.EventID = CntTab.CntID
ORDER BY UserTab.UserID
This gives me:
CountTab
UserTab.Name | EventTab.Name | NoPart
--------------------------------------
Tom | Easter | 2
Tom | Holidays | 1
Tom | ThxGiving | 2
Dick | Easter | 2
Dick | Christmas | 1
Harry | ThxGiving | 2
But how to combine/merge ConcatTab and CountTab into QueryResultTab? I want to retrieve the result table in PHP row by row with mysql_fetch_assco().
Please don't tell me about PDO, etc. I know about it.
The other option - what I try to avoid - is do it within a PHP-loop and use numerous tiny SQL-queries to achieve the result.
Based on your sample data, you want all the rows in the participation table, with information from the dimensions. Then you want a summary of that table.
Here is an approach that uses a subquery in the FROM clause:
SELECT u.name, e.name, p2.numPart, p2.Names
FROM X_Participation p INNER JOIN
X_Users u
ON u.UserID = p.UserID INNER JOIN
X_Events e
ON p.EventID = e.EventID INNER JOIN
(SELECT p2.EventId, COUNT(*) as numPart,
GROUP_CONCAT(u2.name SEPARATOR ', ') as names
FROM X_Participation p2 INNER JOIN
X_Users u2
ON u2.UserID = p2.UserID
GROUP BY p2.EventId
) p2
ON p2.EventId = p.EventId;
Notes:
If you are going to use table aliases (which you should), make them shorter, not longer than the table names.
Don't use subqueries unnecessarily. This is especially true in MySQL which materializes subqueries.
You can put additional conditions in a WHERE clause of the outer query.

mysql: query multiple records in 3 tables?

Good day fellow programmers. I have 3 tables, with following sample records.
tbl_members has:
mem_id | mem_fname | mem_lname
1 | Ryan | Layos
2 | Dhave | Sebastian
3 | Staven | Siegal
4 | Ma Ethel | Yocop
5 | Kelvin | Salvador
6 | Herbert | Ares
tbl_member_status has:
status_id | mem_id | leader_id | process_id
1 | 2 | 1 | 2
2 | 3 | 5 | 3
3 | 4 | 6 | 4
4 | 5 | 1 | 4
5 | 1 | 6 | 4
(tbl_member_status.mem_id is foreign keyed to tbl_members.mem_id, and leader_id is also foreign keyed to tbl_members.mem_id because in my case a member can be a leader. 1 member 1 leader)
tbl_process has:
process_id | process_type
1 | CONSOLIDATION
2 | PRE-ENCOUNTER
3 | ENCOUNTER
4 | POST-ENCOUNTER
(a member has a process to take which i used enum with values: CONSOLIDATION, PRE-ENCOUNTER, ENCOUNTER, POST-ENCOUNTER, etc.)
My question now is the proper sql query in getting the desired output query like this.
tbl_query_result
mem_id | member_fname | member_lname | leader_fname | leader_lname | process_type
2 | dhave | sebastian | Ryan | Layos | PRE-ENCOUNTER
5 | Kelvin | Salvador | Ryan | Layos | POST-ENCOUNTER
do remember that two columns of tbl_member_status is referring to one column of tbl_members that is mem_id.
UPDATE:
what i have done so far:
SELECT member.mem_fname, member.mem_lname, leader.mem_fname, leader.mem_lname, tbl_process.process_type
FROM
tbl_member_status as mem_stats
INNER JOIN
tbl_members as member
INNER JOIN
tbl_members as leader
INNER JOIN
tbl_members ON mem_stats.member_id = member.mem_id
INNER JOIN
tbl_process ON tbl_process.process_id = mem_stats.process_id
WHERE
leader.mem_fname = 'Ryan'
This query gets all record even if the leader.mem_fname is not equal to 'Ryan'
Because when you query. The number of rows in the result matters. Like: if your result is for fname = ryan but then the match for mem.id in table memberstatus is two and then in process table is again two. Inshort you will have 2 rows in final output.
Can you try this :
Select M.member_fname, M.mem_lname P.process_type from tbl_members M, tbl_member_status MS, tbl_process P where M.mem_id = MS.mem_id and MS.process_id = P.process_id and where M.member_fname = 'ryan'
Okay i misunderstood your question at first. I have a solution for you which will improve your database schema. If one member has only one leader and a single leader has many memebers. Then why not create a different table called leader and connect to members table directly? So it will be a one to one relation. Which will make querying much simpler. So now you have 4 tables.
Select M.member_fname, M.mem_lname, L.fname, L.lname, P.process_type
from tbl_members M, tbl_member_status MS, tbl_process P, tbl_leader L
where M.leader_id = L.id and M.mem_id = MS.mem_id and MS.process_id = P.process_id and where M.member_fname = 'ryan'

MySQL multiple left join issues

I have two tables that I am trying to query, and I can only get about half the information I need. The two tables are:
client_skills_new:
+----+-----------+------------+----------+-------------+
| id | client_id | job_sector | job_type | job_name |
+----+-----------+------------+----------+-------------+
| 79 | 24 | 3 | 39 | Accountant |
+----+-----------+------------+----------+-------------+
| 80 | 25 | 3 | 115 | Broker |
+----+-----------+------------+----------+-------------+
| 81 | 24 | 5 | 241 | Shop Worker |
+----+-----------+------------+----------+-------------+
and
job_sectors:
+-----+--------------------------+---------------+
| id | name | job_sector_id |
+-----+--------------------------+---------------+
| 3 | Accounting & Finance | 0 |
+-----+--------------------------+---------------+
| 115 | Brokerage | 3 |
+-----+--------------------------+---------------+
| 22 | Sales & Retail | 0 |
+-----+--------------------------+---------------+
The job sectors table actually contains job sectors and job types in one column (name). job_sector id links the two (ie brokerage is a subheading of accounting and finance - job_sector_id = id).
In the client_skills_new table the numbers stored under job_sector and job_type relate to the id column of job_sectors. What I am trying to do is write a query which will join the two to give me the textual value in the job_sectors table related to the job_sector and job_type integers in client_skills_new.
So far I have a query as follows:
SELECT client_skills_new.job_sector, job_sectors.id, job_sectors.name
FROM job_sectors
LEFT JOIN client_skills_new
ON client_skills_new.job_sector = job_sectors.id
WHERE client_id='$client_id';
From this, I get results as follows:
+------------+----+--------------------------+
| job_sector | id | name |
+------------+----+--------------------------+
| 3 | 3 | Accounting & Finance |
+------------+----+--------------------------+
| 22 | 22 | Sales & Retail |
+------------+----+--------------------------+
Which given id of 24 from my top table gives me about half of what I need. I'd like the query to also include the text name corresponding to job_type. I'm not quite sure what I need to add to finish the query. I tried a second left join but this kept erroring.
The output I'm looking for, given each id in client_skills_new, is as follows:
+----+--------------------------+-----------+----------+
| id | job_sector | job_type | job_name |
+----+--------------------------+-----------+----------+
| 80 | Accounting & Finance | Brokerage | Broker |
+----+--------------------------+-----------+----------+
Yes, you need 2 LEFT joins but the other way around:
SELECT c.id,
js.name AS job_sector,
jt.name AS job_type,
c.job_name
FROM client_skills_new AS c
LEFT JOIN job_sectors AS js
ON c.job_sector = js.id
LEFT JOIN job_sectors AS jt
ON c.job_type = jt.id
WHERE c.client_id='$client_id' ;
The problem is the where clause. It is "undoing" the left outer join, because the values in one table are NULL when there is no match.
The fix is to move the logic to the on clause:
SELECT client_skills_new.job_sector, job_sectors.id, job_sectors.name
FROM job_sectors LEFT JOIN
client_skills_new
ON client_skills_new.job_sector = job_sectors.id and
client_id='$client_id';