我在工作时重构了一些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)
这些何时将不会返回相同的结果?
答案 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
最终答复:
答案 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 JOIN
或LEFT 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)
在您的第一个查询中,您将获得左表的所有行
但是
在第二个位置,当您按位置分组过滤时,只会给出完全填充位置条件的记录