SQL左连接与JOIN条件中的过滤器与WHERE子句中的过滤器[重复]

Posted

技术标签:

【中文标题】SQL左连接与JOIN条件中的过滤器与WHERE子句中的过滤器[重复]【英文标题】:SQL left join with filter in JOIN condition vs filter in WHERE clause [duplicate] 【发布时间】:2019-02-15 17:49:09 【问题描述】:

我正在重构一些工作中的 sql,偶然发现了一些我不知道如何解释的东西。我认为有两个查询会产生相同的结果,但结果并非如此,而且我不确定为什么。

查询如下:

select *
from TableA as a
left join TableB b on a.id = b.id and b.status in (10, 100)

select *
from TableA as a
left join TableB b on a.id = b.id
where b.status is null or b.status in (10, 100)

这些什么时候不会返回相同的结果?

【问题讨论】:

由于您在 2;nd 查询中有 is null 条件,它们将返回相同的结果。 @jarlh 我也这么认为,但他们没有。 在第二个查询中试试where b.id is null or b.status in (10, 100),和第一个一样吗? 糟糕,现在我明白了。你应该改用where b.id is null ... 您的问题主要归结为在ON 子句中过滤status 或在WHERE 子句中过滤。显然,在后一种情况下,您往往会丢失更多记录。 IS NULL 检查没有做任何你打算做的事情;但它也允许匹配的记录恰好具有NULL 状态传递到结果集。 【参考方案1】:

与 Where 条件 b.status is null or b.status in (10, 100) 的最大区别 是当 b.status 是 1 以及 b.id=a.id

在第一个查询中,您仍然会从表 A 中获取相应 B 部分为 NULL 的行,因为 On 条件未完全满足。 在第二个查询中,您将在 JOIN 中获得 a 和 b 表的行,这些行将在 where 子句中丢失。

【讨论】:

这很有意义!我认为从概念上讲,第二个对我来说更有意义,但我必须与一些同事核实。谢谢!【参考方案2】:

让我举个例子:

SELECT * INTO #A FROM (VALUES 
(1),(2),(3),(4)) T(id)

SELECT * INTO #B FROM (VALUES
(1,NULL),
(2,1),
(3,10)) T(id,status)

select *
from #A as a
left join #B b on a.id = b.id and b.status in (10, 100)

select *
from #A as a
left join #B b on a.id = b.id
where b.status is null or b.status in (10, 100)

结果

id          id          status
----------- ----------- -----------
1           NULL        NULL
2           NULL        NULL
3           3           10
4           NULL        NULL

id          id          status
----------- ----------- -----------
1           1           NULL
3           3           10
4           NULL        NULL

最终回复:

    如果状态不在 (10,100) 中,则 LEFT JOIN 应用 NULL 如果状态为 NULL,LEFT JOIN 也适用 NULL,谓词无效

【讨论】:

【参考方案3】:

从逻辑上讲,您的第二个查询几乎与:

SELECT *
FROM TableA as a
LEFT JOIN TableB b
    ON a.id = b.id
WHERE b.status IN (10, 100);  -- b.status is null has been removed

那么问题就归结为ON 子句中的过滤与WHERE 子句中的过滤的标准问题。在前一种情况下,连接左侧的所有记录都将保留,即使ON 逻辑失败。在后一种情况下,即您的第二个查询的情况下,不符合 status 条件的匹配记录将被删除,并且不会显示在结果集中。

我说几乎是一样的,因为您进行的b.status IS NULL 检查实际上会允许记录在连接条件中确实匹配,但发生在status 有一个 null 值。但是,除此之外,您的问题实际上只是在 ON 子句中过滤而不是在 WHERE 子句中进行过滤。

【讨论】:

【参考方案4】:
select * from TableA as a left join TableB b on a.id = b.id and b.status in (10, 100)

条件语句 AND 在连接发生之前进行评估。

select * from TableA as a left join TableB b on a.id = b.id
  where b.status is null or b.status in (10, 100)

过滤器发生在表连接之后。

所以这就是你会得到不同输出的原因。

【讨论】:

【参考方案5】:

因为它是LEFT JOIN,所以不匹配的ON 条件将生成一个行,其中右表中的所有列都包含NULL。 p>

另一方面,不匹配的WHERE 子句将完全消除该行,而不管连接类型如何。考虑这个例子:

CREATE TABLE #TableA(id INT);
INSERT INTO  #TableA VALUES
    (1),
    (2);
CREATE TABLE #TableB(id INT, status INT);
INSERT INTO  #TableB VALUES
    (1, 10),
    (2, -1);

SELECT *
FROM #TableA AS A
LEFT JOIN #TableB B ON A.id = B.id AND B.status IN (10)
/*
    a.id | b.id | status
    1    | 1    | 10
    2    | NULL | NULL
*/    

SELECT *
FROM #TableA AS A
LEFT JOIN #TableB B ON A.id = B.id
-- WHERE B.status IS NULL OR B.status IN (10)
/*
    a.id | b.id | status
    1    | 1    | 10
    2    | 2    | -1
*/

请注意,我已经注释掉了第二个查询中的 where 子句(结果已经不同)。添加后,它也会消除第二行。

【讨论】:

【参考方案6】:

在正常情况下,A LEFT JOINLEFT OUTER JOIN 将左表中的所有行与两个表中的匹配行一起提供。当左表中的一行在右表中没有匹配的行时,关联的结果集行包含来自右表的所有选择列表列的空值。

当我们添加一个带有左外连接的 where 子句时,它的行为就像一个内连接,在 ON 子句之后应用过滤器,只显示那些具有

的行

B.status is null or 10 or 100

【讨论】:

【参考方案7】:

在您的第一个查询中,您将获得左表的所有行

但是

在第二个 where 当你通过 where cluase 过滤时,它只会给出那些完全满足 where 条件的记录

【讨论】:

以上是关于SQL左连接与JOIN条件中的过滤器与WHERE子句中的过滤器[重复]的主要内容,如果未能解决你的问题,请参考以下文章

sql中where的与jnner join on的连接条件.哪个优先级别高?

SQL 查询条件放在LEFT OUTER JOIN 的ON语句后与放在WHERE中的区别

sql join on 与where

SQL文摘:sql(join中on与where区别)

SQL JOIN 中 on 与 where 的区别

SQL JOIN 中 on 与 where 的区别