SQL 在 JOIN 条件中使用过滤器与 WHERE 子句中的过滤器进行左连接

SQL left join with filter in JOIN condition vs filter in WHERE clause

我在工作中重构了一些 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)

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

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

但是

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

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

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

在正常情况下,A LEFT JOINLEFT OUTER JOIN 会给出左侧 table 的所有行以及来自两个 table 的匹配行。当左侧 table 中的一行在右侧 table 中没有匹配行时,关联的结果集行包含来自右侧 table 的所有 select 列表列的空值.

当我们添加带有左外连接的 where 子句时,它的行为类似于内连接,其中过滤器在 ON 子句之后应用,仅显示具有

的行

B.status is null or 10 or 100

举个例子:

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

最终回复:

  1. 如果状态不在 (10,100) 中,LEFT JOIN 应用 NULL
  2. 如果status为NULL,LEFT JOIN也适用NULL,predicate无效

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

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 检查实际上会允许 所做的记录继续存在在连接条件中匹配,但恰好有 statusnull 值。但是,除此之外,您的问题实际上只是 ON 子句中的过滤与 WHERE 子句中的过滤之一。

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)

过滤器在表连接后发生。

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

因为它是 LEFT JOIN,不匹配的 ON 条件将 生成一行,其中从右边 table 开始的所有列都包含NULL.

另一方面,不匹配的 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 子句(结果已经不同了)。添加后,它也会删除第二行。