跨越三个 table 的 LEFT JOIN(有连接点 table)

LEFT JOIN across three tables (with junction table)

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

比如说,我有两个 table,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 的详细信息和 fluffy 的价值观的一行。此外,如果人类 13 没有名为 'fluffy' 的宠物,我将获得包含人类 13 的值的行,以及宠物列的空值。

BUT,我没有直接的FK关系,我在humanspets之间有一个交界点table,所以我正在尝试这样的查询:

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

第 returns 行是人类 13 的所有宠物,除了 fluffy 的行外都是空列。

如果我将 p.name = 'fluffy' 添加到 WHERE 子句,这将过滤掉所有空行,但也意味着如果人类 13 根本没有名为 fluffy 的宠物,我将获得 0 行.

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

一种方法是在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);

或者,将联结 table 和宠物 table 作为子查询或 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;

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

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

Per documentation:

Parentheses can be used around JOIN clauses to control the join order. In the absence of parentheses, JOIN clauses nest left-to-right.

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

SQL Fiddle.

旁白 1:我假设您知道您有 n:m(多对多)关系的教科书实现?

  • How to implement a many-to-many relationship in PostgreSQL?

旁白 2:示例中不幸的命名约定使得有必要处理列别名。不要在实际的 table 中使用 "id" 和 "name" 作为列名,以避免此类冲突。使用专有名称,例如 "pet_id"、"human_id" 等