SQL在JOIN条件中使用过滤器与WHERE子句中的过滤器保持连接

问题描述 投票:3回答:7

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

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

sql sql-server tsql join outer-join
7个回答
5
投票

与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子句中丢失。


4
投票

让我举个例子:

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,谓词无效

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检查你实际上允许记录存活,在连接条件下确实匹配,但恰好有null值为status。但是,除此之外,你的问题实际上只是在ON条款中过滤而不是在WHERE条款中进行过滤。


1
投票

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

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

B.status is null or 10 or 100


1
投票

由于它是LEFT JOIN,不匹配的ON条件将简单地为右手表中的列生成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子句(结果已经不同)。一旦添加,它也将消除第二行。


1
投票
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)

过滤后会发生过滤。

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


0
投票

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

在第二种情况下,当你按where子句过滤时,它只会给那些完全填充where条件的记录

© www.soinside.com 2019 - 2024. All rights reserved.