在JOIN条件下使用过滤器对SQL进行左连接,而在WHERE子句中使用过滤器对SQL进行

时间:2018-09-11 08:56:50

标签: sql sql-server tsql join outer-join

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

这些何时将不会返回相同的结果?

7 个答案:

答案 0 :(得分:5)

Where条件getArguments()的与众不同 是b.status等于1以及b.id = a.id

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

答案 1 :(得分: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. 如果状态为NULL,则LEFT JOIN也适用NULL,谓词无效

答案 2 :(得分: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检查实际上会使记录在联接条件下匹配的 did 幸免于难。恰好具有null的{​​{1}}值。但是,除此之外,您的问题实际上只是在status子句中进行过滤而不是在ON子句中进行过滤的问题之一。

答案 3 :(得分:1)

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

当我们添加带有左外部联接的where子句时,它的行为就像内部联接,在ON子句之后应用过滤器,仅显示具有

的那些行

B.status is null or 10 or 100

答案 4 :(得分: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)

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

所以这就是您获得不同输出的原因。

答案 5 :(得分:1)

由于它是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 :(得分:0)

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

但是

在第二个位置,当您按位置分组过滤时,只会给出完全填充位置条件的记录