Sql 使用条件跨多对多表执行左联接

Sql 使用条件跨多对多表执行左联接,sql,mysql,Sql,Mysql,一般情况 当您要向关系的外部添加条件时,如何跨多对多关系执行左联接 具体案例 我们正在处理两个表:team和pool。还有一个team\u pool表作为它们之间的多对多连接表。此外,pool还有一个stage\u id列 我想检索在特定阶段拥有该团队资源库的所有团队。如果池不存在,我希望池为NULL。理想化结果样本: +--------+----------+------+ | team | stage_id | pool | +--------+----------+------+ |

一般情况

当您要向关系的外部添加条件时,如何跨多对多关系执行左联接

具体案例

我们正在处理两个表:
team
pool
。还有一个
team\u pool
表作为它们之间的多对多连接表。此外,
pool
还有一个
stage\u 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));
理想溶液

理想的解决办法是:

  • 不需要更改我的模式(ORM真的希望这样做)
  • 需要单个非联合查询
尝试的解决方案

  • 使用内部联接,而不是从
    team
    team\u pool
    team\u pool
    pool
    的左联接。问题:我们失去了不属于游泳池的球队
  • 左键从
    team
    连接到
    team\u pool
    team\u pool
    连接到
    pool
    ,条件是
    pool
    上的
    stage\u id
    与我们要查找的匹配。问题:当一个团队属于多个池时,我们会得到多个结果。通过添加组没有帮助
编辑:选择的解决方案

这里有很多好的解决方案

考虑到我的理想解决方案不可能实现,我宁愿将
stage\u id
添加到team\u池中,而不是使用联合或子查询。这样做的另一个好处是,我可以强制执行团队每个阶段只能属于一个池。它还使查询变得简单:

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

简而言之,不可能满足你所有的标准

更详细的回答:通过一个工会,你很有可能获得所有的结果。上半场将展示在该阶段有游泳池的球队;第二个将显示不需要的团队

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

如果我理解您模式背后的概念,那么我认为
stage\u id
应该是
team\u pool
中的一列,而不是
pool
。阶段不是池的属性,它是团队映射到池的一个因素,对吗

不管怎样,这就是我在Oracle中编写查询的方式。我不确定这种语法是否适合MySQL。您可能希望参数化
stage\u 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

如果您只需要一个阶段,您可以将表连接在一起。如果找不到池,则池名称将为空。例如,对于
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
如果要查询所有阶段,可以使用
交叉联接
为每个团队阶段组合生成一行。然后,左联接为每行搜索一个池:

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\u id
来自交叉连接,如果找不到池,它将不会是
null

不知道这是否可行,因为我现在没有可以使用的数据库,但我认为这应该可行。基本上创建两个左连接的“表”,然后再次左连接它们。如果它真的起作用,我无法想象它会有多好的表现

更新 简化了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
我们很清楚:

你想提供一个团队和一个舞台

您要返回: 所有团队在提供的阶段与您提供的团队共享任何池 在提供的阶段没有游泳池的所有球队 在所提供的阶段,与您提供的团队共享任何池的任何团队的所有池/团队组合

我很确定这可以在没有工会的情况下完成,但我不完全清楚你想要什么结果

首先,海事组织:

您的初始选择应该在团队表上

您应该在
JOIN
子句而不是
WHERE
子句中为池添加条件


一旦有了所需的阶段,您应该再次加入团队表,以介绍所有团队池

如果池为空,团队2如何拥有阶段id?您能否将其添加到理想结果中,以显示如果一个团队属于多个池,您的期望值?阶段是池的一个属性(池存在于某个阶段)。如果在联合和将stage_id添加到team_池之间进行选择,我将选择后者。我避免将stage_id添加到team_pool的主要原因是,我使用的ORM使这样做有点烦人。SQL摘要:将pool和team_pool连接在一起,就像一个表,然后使用team_id将team连接到新的伪表。将pool连接到team_pool可能使用内部连接;这只是一种将team\u id链接到pool\u id的方法。一旦建立了这个方法,那么您应该能够将team.team\u id链接到任何p.team\u id,而不需要重复(我认为)。