一般情况
如果要在关系的外方添加条件,如何在多对多关系中执行左连接?
具体案例
我们正在处理两个表:team
和pool
。还有一个team_pool
表作为它们之间的多对多联结表。此外,pool
有一个stage_id
列。
我希望在特定阶段检索具有该团队池的所有团队。如果池不存在,我希望池为NULL。样本,理想化结果:
+--------+----------+------+
| 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 |
如果相关,我正在使用MySQL。
SQL
以下是用于创建表的(简化)SQL:
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));
理想解决方案
理想的解决方案是:
尝试解决方案
team
到team_pool
和team_pool
到pool
的LEFT JOIN。问题:我们失去了不属于游泳池的团队team
team_pool
和team_pool
左右加入@ pool
,stage_id
pool
与我们'匹配正在寻找。问题:当一个团队属于许多池时,我们会得到多个结果。添加GROUP BY无济于事。编辑:选择解决方案
这里有很多好的解决方案。
鉴于我的理想解决方案是不可能的,我宁愿将stage_id
添加到team_pool而不是使用UNION或子查询。这有一个额外的好处,让我强制一个团队每个阶段只能属于一个池。它还使查询变得简单:
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
答案 0 :(得分:1)
简短回答,无法满足您的所有标准。
更长的答案:使用UNION可以获得所有结果。上半场将展示那个阶段有球队的球队;第二个将显示没有的团队。
select ...
from team
left join team_pool
on team.id = team_pool.team_id
left-join pool
on pool.id = team_pool.pool_id
and pool.stage_id = @stage
UNION
select distinct team.id, @stage, NULL
from team
where not exists
(select pool_id
from pool
inner join team_pool
on team_pool.pool_id = pool.pool_id
where team_pool.team_id = team.id
and pool.stage_id = @stage
答案 1 :(得分:1)
如果我了解您的架构背后的概念,那么我认为stage_id
应该是team_pool
而不是pool
的列。阶段不是池的属性,它是将团队映射到池的一个因素,对吧?
无论如何,这就是我在Oracle中编写查询的方式。我不确定这种确切的语法是否适合MySQL。大概你想要参数化stage_id
的文字值。
SELECT t.name, p.name
FROM (SELECT team.name, pool_id
FROM team LEFT JOIN team_pool
ON team_pool.team_id = team.team_id ) t
LEFT JOIN (SELECT pool_id, name FROM pool WHERE stage_id = 2) p
ON p.pool_id = t.pool_id
答案 2 :(得分:1)
如果您只想找一个舞台,可以将桌子连在一起。如果未找到池,则池名称将为null。例如,对于stage = 2
:
select t.name, 2, pool.name
from team t
left join
(
select team_id, p.name
from team_pool tp
join pool p
on tp.pool_id = p.id
where p.stage_id = 2
) pool
on pool.team_id = t.id
如果您想查询所有阶段,可以使用cross join
为每个团队阶段组合生成一行。然后左连接搜索每行的池:
select t.name, s.stage_id, p.name
from team t
cross join
(
select distinct stage_id
from pool
) s
left join
(
select tp.team_id, p.stage_id, p.name
from team_pool tp
join pool p
on tp.pool_id = p.id
) pool
on pool.team_id = t.id
and pool.stage_id = s.stage_id
由于stage_id
来自交叉联接,如果找不到合并,则不会是null
。
答案 3 :(得分:0)
不知道这是否有效,因为我现在没有可以玩的数据库,但我认为这应该有效。基本上创建两个左连接“表”,然后再次加入它们。如果它确实有效,我无法想象它会非常高效。
的更新强> 的 简化了SQL
SELECT t.name, p.stage_id, p.pool_name
FROM team t
LEFT JOIN
(SELECT pool.name as pool_name, pool.stage_id, team_pool.team_id as junc_team_id
FROM pool LEFT JOIN team_pool
ON team_pool.pool_id = pool.pool_id) p
ON p.junc_team_id = t.team_id
答案 4 :(得分:0)
我们很清楚:
您想提供一个团队和一个舞台
您想要返回: 所有团队在提供的阶段与您提供的团队共享任何池 所有在提供的阶段都没有游泳池的球队 在提供的舞台上,与您提供的团队共享任何游泳池的团队的所有游泳池/团队组合?
我很确定这可以在没有UNION
的情况下完成,但我不完全清楚你的结果是什么
作为首发,IMO:
您的初始选择应该在球队表
上您应该在JOIN
子句而不是WHERE
子句中添加池的条件
一旦你有一个所需的阶段来介绍所有的团队池,你应该再次加入团队表