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 JOIN
或 LEFT 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
最终回复:
- 如果状态不在 (10,100) 中,LEFT JOIN 应用 NULL
- 如果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
检查实际上会允许 所做的记录继续存在在连接条件中匹配,但恰好有 status
的 null
值。但是,除此之外,您的问题实际上只是 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 子句(结果已经不同了)。添加后,它也会删除第二行。
我在工作中重构了一些 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 JOIN
或 LEFT 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
最终回复:
- 如果状态不在 (10,100) 中,LEFT JOIN 应用 NULL
- 如果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
检查实际上会允许 所做的记录继续存在在连接条件中匹配,但恰好有 status
的 null
值。但是,除此之外,您的问题实际上只是 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 子句(结果已经不同了)。添加后,它也会删除第二行。