跨越三个 table 的 LEFT JOIN(有连接点 table)
LEFT JOIN across three tables (with junction table)
在 Postgres 中,有没有办法在由联结点 table 链接的 table 之间执行 left join
,并对链接的 table 进行一些过滤?
比如说,我有两个 table,humans
和 pets
,我想在我有人类 ID 和宠物名称的地方执行查询。如果人类ID存在,但他们没有该名字的宠物,我仍然希望返回人类的行。
如果我有从 pets
到 humans
的 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关系,我在humans
和pets
之间有一个交界点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;
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
.
旁白 1:我假设您知道您有 n:m(多对多)关系的教科书实现?
- How to implement a many-to-many relationship in PostgreSQL?
旁白 2:示例中不幸的命名约定使得有必要处理列别名。不要在实际的 table 中使用 "id" 和 "name" 作为列名,以避免此类冲突。使用专有名称,例如 "pet_id"、"human_id" 等
在 Postgres 中,有没有办法在由联结点 table 链接的 table 之间执行 left join
,并对链接的 table 进行一些过滤?
比如说,我有两个 table,humans
和 pets
,我想在我有人类 ID 和宠物名称的地方执行查询。如果人类ID存在,但他们没有该名字的宠物,我仍然希望返回人类的行。
如果我有从 pets
到 humans
的 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关系,我在humans
和pets
之间有一个交界点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;
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
.
旁白 1:我假设您知道您有 n:m(多对多)关系的教科书实现?
- How to implement a many-to-many relationship in PostgreSQL?
旁白 2:示例中不幸的命名约定使得有必要处理列别名。不要在实际的 table 中使用 "id" 和 "name" 作为列名,以避免此类冲突。使用专有名称,例如 "pet_id"、"human_id" 等