我想知道为什么以下两个查询产生不同的结果(第一个查询的行数多于第二个)。
SELECT * FROM A
JOIN ...
JOIN ...
JOIN C ON ...
LEFT JOIN B ON B.id = A.id AND B.otherId = C.otherId
相反:
SELECT * FROM A
JOIN ...
JOIN ...
JOIN C ON ...
LEFT JOIN B ON B.id = A.id
WHERE B.otherId = C.otherId
请帮我理解。在第二个查询中,左连接只有1个条件,因此它不应包含第一个查询的所有结果以及更多(其他行具有不匹配的otherId
)。然后WHERE
子句应确保otherId
匹配,就像在第一个查询中一样。他们为什么不同?
答案 0 :(得分:2)
在执行WHERE
之前,JOIN
首先由查询引擎执行
如果我们稍后要过滤某些行,原因就是为什么要使用昂贵的JOIN
。
查询引擎非常擅长优化您编写的查询。
此外,您只会在OUTER JOIN
s中看到此效果。在内连接中,WHERE
和JOIN
条件的行为相同。
答案 1 :(得分:1)
第二个查询返回的行数较少,因为您的where
子句正在过滤掉记录,这实质上是将查询从左外连接更改为内连接。因此,您需要小心放置过滤器的位置,但如果您要进行内部连接则无关紧要。
答案 2 :(得分:0)
您已收到正确的答案,但请允许我深入研究加入条件和过滤条件之间的区别。使用左连接进行简单查询:
select a.Key, a.NonKey1, b.NonKey2
from a
left join b on b.Key = a.Key;
这会列出表NonKey1
中的所有a
值以及表NonKey2
中具有匹配键值的所有b
字段,或NULL
没有匹配键值比赛。一个常见的变体是只查看a
中b
中没有匹配项的那些行:
select a.Key, a.NonKey1, b.NonKey2
from a
left join b on b.Key = a.Key
where b.Key is null;
小心!如果您不小心写了where b.Key is not null
,那么您刚刚将外部联接更改为常规内部联接。这样做,看看质量保证是否能抓住它。第二个想法,不要。 (此外,在选择列表中使用b.NonKey2
是没有意义的,因为它只能是NULL
,但暂时让它保留在那里。)连接基于两个表匹配的关键字段。加入完成后,将丢弃所有成功加入的行,并且仅保留没有匹配的结果。这意味着加入条件中的b.Key
不能为NULL
,而过滤条件必须为NULL
才能添加一行到结果集。好的,这就是我们想要的。但是考虑如果我们将支票移动到加入标准的一部分会发生什么。
select a.Key, a.NonKey1, b.NonKey2
from a
left join b on b.Key = a.Key and b.Key is null;
结果是来自a
的所有内容,b
完全没有任何内容。可能不是我们想要的。如果你仔细想想,你会发现我们也可以写on 0 = 1
并得到相同的结果。我们所做的是从一个上下文中移动一个值,其中NULL
表示一件事(成功)到NULL
表示完全不同(失败)的上下文。
因此,在人类语言的计算机语言中,要注意上下文。它可以彻底改变你想说的内容。