使用NOT IN子句的子查询成功时,LEFT JOIN是否可能失败?

时间:2013-10-24 23:53:18

标签: sql postgresql left-join in-clause notin

我已经发布了这个问题的答案PostgreSQL multiple criteria statement

任务非常简单 - 如果另一个表中没有相应的值,则从一个表中选择值。假设我们有如下表:

CREATE TABLE first  (foo numeric);
CREATE TABLE second (foo numeric);

我们希望从first.foo获取second.foo中未出现的所有值。我提出了两个解决方案:

  • 使用LEFT JOIN
SELECT first.foo
FROM first
LEFT JOIN  second 
ON first.foo = second.foo
WHERE second.foo IS NULL;
  • 合并子查询和IN运算符:
SELECT first.foo 
FROM first
WHERE first.foo NOT IN (
  SELECT second.foo FROM second
);

出于某种原因,第一个在OP的上下文中不起作用(返回0行),从那以后它一直在困扰我。我试图使用不同版本的PostgreSQL重现该问题但到目前为止没有运气。

第一个解决方案失败并且第二个解决方案按预期工作是否有任何特殊原因?我错过了一些明显的东西吗

Here是sqlfiddle,但它似乎适用于任何可用的平台。

修改

像@bma和@MostyMostacho一样,在评论中指出它应该是第二个没有返回结果的(sqlfiddle)。

2 个答案:

答案 0 :(得分:2)

根据你的sql小提琴,由于第二个表中的NULL,你的NOT IN查询无法返回结果。

问题是NULL表示“UNKNOWN”,因此我们不能说下面的表达式为真:10 not in (5, null)

原因是比较10 = NULL时会发生什么。我们得到一个NULL,而不是真的。这意味着NOT IN子句中的NULL意味着不会传递任何行。

要让第二个按预期方式执行,您会遇到相对复杂的查询:

SELECT first.foo 
FROM first
WHERE (first.foo  IN (
  SELECT second.foo FROM second
) IS NOT TRUE);

这将正确处理NULL比较,但是连接语法可能更清晰。

答案 1 :(得分:1)

api/assets您刚回答了自己的问题:

select values from one table if there is no corresponding value in another table.

简短演示:

SELECT o.value
FROM table_one o
WHERE NOT EXISTS (
    SELECT *
    FROM table_two t
    WHERE t.value = o.value
    );

结果:

CREATE TABLE first  (foo numeric);
CREATE TABLE second (foo numeric);

INSERT INTO first VALUES (1);
INSERT INTO first VALUES (2);
INSERT INTO first VALUES (3);
INSERT INTO first VALUES (4);
INSERT INTO first VALUES (5);
INSERT INTO first VALUES (NULL); -- added this for completeness

INSERT INTO second VALUES (1);
INSERT INTO second VALUES (3);
INSERT INTO second VALUES (NULL);


SELECT f.foo AS ffoo, s.foo AS sfoo
        -- these expressions all yield boolean values
        , (f.foo = s.foo)                                               AS is_equal
        , (f.foo IN (SELECT foo FROM second))                           AS is_in
        , (f.foo NOT IN (SELECT foo FROM second))                       AS is_not_in
        , (EXISTS (SELECT * FROM second x WHERE x.foo = f.foo))         AS does_exist
        , (NOT EXISTS (SELECT * FROM second x WHERE x.foo = f.foo))     AS does_not_exist
        , (EXISTS (SELECT * FROM first x LEFT JOIN second y ON x.foo = y.foo
                WHERE x.foo = f.foo AND y.foo IS NULL))
                                                                        AS left_join_is_null
FROM first f
FULL JOIN second s ON (f.foo = s.foo AND (f.foo IS NOT NULL OR s.foo IS NOT NULL) )
        ;

如您所见,CREATE TABLE CREATE TABLE INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 ffoo | sfoo | is_equal | is_in | is_not_in | does_exist | does_not_exist | left_join_is_null ------+------+----------+-------+-----------+------------+----------------+------------------- 1 | 1 | t | t | f | t | f | f 2 | | | | | f | t | t 3 | 3 | t | t | f | t | f | f 4 | | | | | f | t | t 5 | | | | | f | t | t | | | | | f | t | f | | | | | f | t | f (7 rows) 等于情况下布尔值可以为NULL。 IN()情况不能为NULL。生存还是毁灭。 EXISTS()(几乎)等同于NOT EXISTS情况,除了它实际上包含LEFT JOIN ... WHERE s.foo IS NULL到查询结果中(在大多数情况下不需要)