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 啥区别?

SQL中的left outer join,inner join,right outer join用法详解

SQL Server 中的 LEFT JOIN 与 LEFT OUTER JOIN