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 JOIN
或 LEFT 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的连接条件.哪个优先级别高?