LEFT OUTER JOIN with 'field IS NULL' in WHERE 用作 INNER JOIN
Posted
技术标签:
【中文标题】LEFT OUTER JOIN with \'field IS NULL\' in WHERE 用作 INNER JOIN【英文标题】:LEFT OUTER JOIN with 'field IS NULL' in WHERE works as INNER JOINLEFT OUTER JOIN with 'field IS NULL' in WHERE 用作 INNER JOIN 【发布时间】:2021-11-29 11:58:39 【问题描述】:今天我在 PostgreSQL 中遇到了一些(对我来说)无法解释的行为——LEFT OUTER JOIN
不会返回主表的记录(连接一个字段为空值),以防在WHERE
表达式中使用连接表字段。
为了更容易掌握案例细节,我将提供一个示例。所以,假设我们有两张表:item
有一些商品,price
指的是item
,有不同年份的商品价格:
CREATE TABLE item(
id INTEGER PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE price(
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
year INTEGER NOT NULL,
value INTEGER NOT NULL,
CONSTRAINT goods_fk FOREIGN KEY (item_id) REFERENCES item(id)
);
item
表有 2 条记录(电视机和 VCR 项目),price
表有 3 条记录,2000 年和 2010 年的电视机价格,以及 2000 年的 VCR 价格:
INSERT INTO item(id, name)
VALUES
(1, 'TV set'),
(2, 'VCR');
INSERT INTO price(id, item_id, year, value)
VALUES
(1, 1, 2000, 290),
(2, 1, 2010, 270),
(3, 2, 2000, 770);
-- no price of VCR for 2010
现在让我们进行 LEFT OUTER JOIN
查询,以获取 2010 年所有商品的价格:
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN price p ON i.id = p.item_id
WHERE p.year = 2010 OR p.year IS NULL;
由于某种原因,此查询将只返回电视机的结果,该电视机有今年的价格。结果中没有 VCR 的记录:
id | name | year | value
----+--------+------+-------
1 | TV set | 2010 | 270
(1 row)
经过一些试验,我找到了一种方法来进行查询以返回我需要的结果(item
表的所有记录,在连接表的字段中使用空值,以防当年没有数学记录。这是通过将年份过滤移动到JOIN
条件来实现的:
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN (
SELECT * FROM price
WHERE year = 2010 -- <= here I filter a year
) p ON i.id = p.item_id;
现在结果是:
id | name | year | value
----+--------+------+-------
1 | TV set | 2010 | 270
2 | VCR | |
(2 rows)
我的主要问题是——为什么第一个查询(WHERE
中的年份过滤)没有按预期工作,而是变成了类似INNER JOIN
的东西?
在我当前的项目中,这个问题严重阻碍了我,所以我也会感谢关于下一个相关问题的提示/提示:
-
是否有其他选择可以达到正确的结果?
...尤其是 — 可以轻松转换为 Django 的 ORM 查询集?
更新:@astentx 建议将过滤条件直接移动到JOIN
(也可以):
SELECT
i.*,
p.year,
p.value
FROM item i
LEFT OUTER JOIN price p
ON
i.id = p.item_id
AND p.year = 2010;
虽然与我的第一个解决方案相同,但我看不出如何用 Django ORM 查询集来表达它。还有其他建议吗?
【问题讨论】:
VCR 的行由left join
连接,year
不是 null 或 2010。这就是它被过滤掉的原因。您可以将当年的where
条件(不带is null
)移动到join
条件以实现您想要的(不带子查询)
@astentx 是的,它有效。虽然,和我的第一个解决方案一样,我不知道如何用 Django 查询集来表达它(但会努力的,谢谢!)
@astentx 还有,如果你的回答完全涵盖了我的主要问题。
对不起,我对 Django 不熟悉。您必须研究如何使用它来自定义连接条件。
【参考方案1】:
第一个查询没有按预期工作,因为预期错误。它也不能作为 INNER JOIN 工作。只有当 VCR 根本没有价格时,查询才会返回 VCR 的记录。
【讨论】:
据我了解,即使某年没有商品价格,您也需要按年返回商品价格 - 在这种情况下,价格未知(NULL)。您是否有一些包含年份列表的时间表,或者您可以即时生成此列表?如果是,您可以使用 item_id 和 year 的 item 表和 years 表/列表的笛卡尔积来 LEFT JOIN 价格表。【参考方案2】:SELECT
i.*,
y.year,
p.value
FROM item i
CROSS JOIN (SELECT 2010 AS year) y -- here could be a table
LEFT OUTER JOIN price p
ON (p.item_id = i.id
AND p.year = y.year);
【讨论】:
以上是关于LEFT OUTER JOIN with 'field IS NULL' in WHERE 用作 INNER JOIN的主要内容,如果未能解决你的问题,请参考以下文章
csharp 来自http://stackoverflow.com/questions/1122942/linq-to-sql-left-outer-join-with-multiple-join-c
关于mysql中的left join和left outer join的区别
MySQL 数据库中 left outer join 和 left join 啥区别
MySQL 数据库中 left outer join 和 left join 啥区别?