Sometimes in a single query, it is required to join different tables based on a condition in one of the tables.
For example, you need to get all persons participating in a contest as individuals or as members of a team. Contest table points either to Team or Person table depending on the participant type:
Persons | TeamPersons | ContestParticipants | |||
Id | Team_Id | Participant_Id | |||
Name | Person_Id | Participant_Type |
SQL:
-- Persons who can participate as individuals or team members CREATE TABLE persons (id INT, name VARCHAR(30)); -- Persons who participate as a team CREATE TABLE teamPersons (team_id INT, person_id INT); -- Type 'P' points to Person, type 'T' to teamPersons (team_id) CREATE TABLE contestParticipants (participant_id INT, participant_type CHAR);
Sample Data:
-- There are 3 persons: Dan, Tom and Steve INSERT INTO persons VALUES (1, 'Dan'), (2, 'Tom'), (3, 'Steve'); -- Dan and Tom are in Team 1 INSERT INTO teamPersons VALUES (1, 1), (1, 2); -- Team 1 and Steve participate in contest INSERT INTO contestParticipants VALUES (1, 'T'), (3, 'P');
So how to get all persons participating in the contest in a single SQL query?
You can use UNION clause to combine the results from several queries into one:
SELECT p.name FROM contestParticipants c, persons p WHERE participant_type = 'P' AND participant_id = p.id UNION ALL SELECT p.name FROM contestParticipants c, teamPersons tp, persons p WHERE participant_type = 'T' AND participant_id = tp.team_id AND tp.person_id = p.id;
The query returns:
name |
Steve |
Dan |
Tom |
Explanation:
It is possible to get the same result without UNION:
SELECT IFNULL(p.name, p2.name) FROM contestParticipants c LEFT OUTER JOIN persons p ON (c.participant_id = p.id AND c.participant_type = 'P') LEFT OUTER JOIN teamPersons tp ON (c.participant_id = tp.team_id AND c.participant_type = 'T') LEFT OUTER JOIN persons p2 ON tp.person_id = p2.id;
The query returns:
name |
Dan |
Tom |
Steve |
Explanation:
Firstly you get names of individuals by joining ContestParticipant and Person tables, but you use LEFT OUTER JOIN so Team rows remains:
SELECT * FROM contestParticipants c LEFT OUTER JOIN persons p ON (c.participant_id = p.id AND c.participant_type = 'P')
Result:
participant_id | participant_type | p.id | p.name |
1 | T | NULL | NULL |
3 | P | 3 | Steve |
Then we get ID of persons participating as team members:
SELECT * FROM contestParticipants c LEFT OUTER JOIN persons p ON (c.participant_id = p.id AND c.participant_type = 'P') LEFT OUTER JOIN teamPersons tp ON (c.participant_id = tp.team_id AND c.participant_type = 'T')
Result:
participant_id | participant_type | p.id | p.name | tp.team_id | tp.person_id |
1 | T | NULL | NULL | 1 | 1 |
1 | T | NULL | NULL | 1 | 2 |
3 | P | 3 | Steve | NULL | NULL |
The we get person names participating as team members:
SELECT * FROM contestParticipants c LEFT OUTER JOIN persons p ON (c.participant_id = p.id AND c.participant_type = 'P') LEFT OUTER JOIN teamPersons tp ON (c.participant_id = tp.team_id AND c.participant_type = 'T') LEFT OUTER JOIN persons p2 ON tp.person_id = p2.id;
Result:
participant_id | participant_type | p.id | p.name | tp.team_id | tp.person_id | p2.id | p2.name |
1 | T | NULL | NULL | 1 | 1 | 1 | Dan |
1 | T | NULL | NULL | 1 | 2 | 2 | Tom |
3 | P | 3 | Steve | NULL | NULL | NULL | NULL |
Finally you use IFNULL(p.name, p2.name) to select the person name as an individual or team member.