LEFT JOIN跨三个表(带连接表)

时间:2015-02-23 23:06:26

标签: sql database postgresql many-to-many left-join

在Postgres中,有没有办法在通过联结表链接的表之间执行left join,并对链接表进行一些过滤?

说,我有两个表,humanspets,我想执行查询,其中包含人员ID和宠物名称。如果人类ID存在,但他们没有带有该名称的宠物,我仍然希望返回人类的行。

如果我有petshumans之间的FK关系,这样可行:

select h.*, p.*
from humans as h
left join pets as p on p.human_id = h.id and p.name = 'fluffy'
where h.id = 13

我会得到一个人类13的细节和蓬松的价值观。此外,如果人类13没有一只名为“蓬松”的宠物,我会得到一行人类13的值,以及宠物列的空值。

但是,我没有直接的FK关系,我在humanspets之间有一个联结表,所以我正在尝试查询: / p>

select h.*, p.*
from humans as h
left join humans_pets_junction as j on j.human_id = h.id
left join pets as p on j.pet_id = p.id and p.name = 'fluffy'
where h.id = 13

返回所有13个宠物的行,除了蓬松的行外,其余的列都是空的。

如果我将p.name = 'fluffy'添加到WHERE子句中,过滤掉所有空行,但也意味着如果人类13没有一个名为蓬松的宠物,我会得到0行。< / p>

有没有办法复制FK样式left join的行为,但是当与联结表一起使用时?

2 个答案:

答案 0 :(得分:2)

一种方法是在where子句中进行比较:

select h.*, p.*
from humans as h left join
     humans_pets_junction as j
     on j.human_id = h.id left join
     pets as p
     on j.pet_id = p.id and p.name = 'fluffy'
where h.id = 13 and (p.name = 'fluffy' or p.id is null);

或者,将联结表和宠物表作为子查询或CTE加入:

select h.*, p.*
from humans h left join
     (select j.*
      from humans_pets_junction j join
           pets p
           on j.pet_id = p.id and p.name = 'fluffy'
     ) pj
     on pj.human_id = h.id
where h.id = 13;

答案 1 :(得分:1)

在Postgres中,您可以使用括号来确定JOIN订单的优先顺序。您不需要子查询:

SELECT h.*, p.id AS p_id, p.name AS pet_name
FROM   humans  h
LEFT   JOIN (pets p
       JOIN  humans_pets_junction j ON p.name = 'fluffy'
                                   AND j.pet_id = p.id
                                   AND j.human_id = 13) ON TRUE
WHERE  h.id = 13;

Per documentation:

  

可以在JOIN子句周围使用括号来控制连接顺序。   在没有括号的情况下,JOIN条款从左到右嵌套。

我将谓词j.human_id = 13添加到您的联结表和宠物之间的联接中,以尽早消除不相关的行。外LEFT JOIN只需要虚拟条件ON TRUE

SQL Fiddle.

除了1:我假设你知道你有一个n:m(多对多)关系的教科书实现?

除了2:示例中的不幸命名约定使得必须处理列别名。不要使用&#34; id&#34;和&#34;名称&#34;作为实际表中的列名以避免此类冲突。使用适当的名称,如&#34; pet_id&#34;,&#34; human_id&#34;等