General Case
How do you perform a left join across a many-to-many relationship when you want to add a condition to the foreign side of the relation?
Specific Case
We're dealing with two tables: team
and pool
. There is also a team_pool
table serving as a many-to-many junction table between them. Additionally, a pool
has a stage_id
column.
I want to retrieve all teams with that team's pool for a specific stage. If the pool doesn't exist, I want the pool to be NULL. Sample, idealized results:
+--------+----------+------+
| team | stage_id | pool |
+--------+----------+------+
| Team 1 | 2 | C |
| Team 2 | NULL | NULL | //this team doesn't belong to a pool for this stage (but it could belong to a pool for a different stage)
| Team 3 | 2 | G |
| Team 3 | 2 | H | //if a team belongs to a 2 pools for the same stage
| Team 4 | 2 | D |
If it's relevant, I'm using MySQL.
SQL
Here's the (simplified) SQL used to create the tables:
CREATE TABLE team (id BIGINT AUTO_INCREMENT, name VARCHAR(50), PRIMARY KEY(id));
CREATE TABLE team_pool (team_id BIGINT, pool_id BIGINT, PRIMARY KEY(team_id, pool_id));
CREATE TABLE pool (id BIGINT AUTO_INCREMENT, stage_id BIGINT, name VARCHAR(40) NOT NULL, PRIMARY KEY(id));
Ideal Solution
The ideal solution would:
- Not require a change to my schema (ORM really wants it this way)
- Require a single, non UNION query.
Attempted solutions
- Use an INNER JOIN rather than a LEFT JOIN from
team
toteam_pool
andteam_pool
topool
. Issue: we lose teams that don't belong to a pool - LEFT JOIN from
team
toteam_pool
andteam_pool
topool
using a WITH condition that thestage_id
onpool
matches what we're looking for. Issue: when a team belongs to many pools, we get multiple results. Adding a GROUP BY doesn't help.
EDIT: Chosen Solution
There are a lot of good solutions here.
Given that my ideal solution is not possible, I'd rather add stage_id
to team_pool than use UNION or subqueries. This has the added benefit of letting me enforce that a team can only belong to one pool per stage. It also makes the queries simple:
SELECT t.name, p.name, tp.stage_id FROM team t LEFT JOIN team_pool tp ON t.id = tp.team_id AND tp.stage_id = 2 LEFT JOIN pool p ON p.id = tp.pool_id