JOIN或WHERE内的条件


191

将条件放入JOIN子句与WHERE子句之间是否有任何区别(性能,最佳实践等)?

例如...

-- Condition in JOIN
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND CUS.FirstName = 'John'

-- Condition in WHERE
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE CUS.FirstName = 'John'

您更喜欢哪一个(也许是为什么)?


4
您是否运行了两个查询?您是否检查了两个查询生成的执行计划?你观察到了什么?
S.Lott

21
@ S.Lott,此查询仅用于示例目的。我只是想知道“一般”是首选方法-如果有的话。
史蒂夫·迪格南

1
@Steve Dignan:您应该使用示例数据对此进行基准测试,并查看查询计划。答案将非常非常清楚。而且-好处-当出现更复杂的情况时,您将有一段代码可以重用。
S.Lott

1
如果条件描述了关系,我个人将条件放在JOIN子句中。然后,仅过滤结果集的通用条件将进入WHERE部分。EGFROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
Glutexo

Answers:


153

关系代数允许WHERE子句和中的谓词可互换INNER JOIN,因此即使INNER JOIN带有WHERE子句的查询也可以使谓词由优化程序重新排列,以便在处理过程中将它们排除在外JOIN

我建议您以最易读的方式编写查询。

有时,这包括使INNER JOIN相对“不完整”并将某些条件置于WHERE简单位置,以使过滤条件列表更易于维护。

例如,代替:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

写:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

但这当然取决于。


7
这不仅与干净的查询或可读性有关,还与性能有关。通过适当地建立索引表,将条件置于连接中可提高大量数据的性能。
沙阿达特(Shahdat)

1
我只是运行每月销售报告,并在几百万条记录上加入5-6张桌子。性能提高30%
-SQL

2
@Shahdat,如果您将性能从过滤条件从where子句移动到内部联接有显着的性能差异,则需要发布这些执行计划。
卡德·鲁

4
@Cade我已经研究了执行计划-两种方案都显示出相同的成本。我多次运行查询似乎都花费相同的时间。以前,我在生产环境上运行查询,由于实时用户正在使用数据库,因此性能差异很大。抱歉让您感到困惑。
Shahdat

4
此答案对INNER JOIN是正确的,但对于左/右联接则不正确。
sotn

121

对于内部联接,我并没有真正注意到差异(但是与所有性能调优一样,您需要根据情况检查数据库)。

但是,如果您使用的是左联接或右联接,则放置条件的位置将产生巨大的差异。例如,考虑以下两个查询:

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderDate >'20090515'

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND ORD.OrderDate >'20090515'

第一个将只给您提供日期晚于2009年5月15日的记录,从而将左联接转换为内部联接。第二个将提供这些记录以及所有没有订单的客户。结果集根据放置条件的不同而有很大差异。(如果仅出于示例目的,请选择*,当然,您不应该在生产代码中使用它。)当您只想查看一个表中的记录而不是另一个表中的记录时,例外。然后,对条件而不是联接使用where子句。

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderID is null

感谢您与例子解释
Rennish约瑟夫

1
“因此将左联接转换为内部联接”。怎么样?您能详细说明一下吗?
user1451111 '18

@ user1451111了解LEFT / RIGHT JOIN返回的内容:INNER JOIN行加上不匹配的左/右表行,这些行由NULL扩展。FULL JOIN返回INNER JOIN行UNION ALL左右不匹配的左,右表行,扩展为NULL。始终知道要作为OUTER JOIN的一部分的INNER JOIN。在WHERE或ON上,在OUTER JOIN ON上要求可能扩展为NULL的列不为NULL之前,删除所有扩展为NULL的行,即仅保留INNER JOIN行,即“将OUTER JOIN转换为INNER JOIN”。
philipxy

1
@ user1451111,或更简单地说:A left join B是将A中的每一行都与B中的每个匹配行连接在一起。如果B没有匹配的行,则A列具有值,但是B上该行的每一列都显示为NULL值。如果您已经写过,where B.somecolumn = ‘somevalue’那么将有一个NULL(B.somecolumn)与'somevalue'进行比较。任何与NULL比较的结果都是错误的,因此将消除所有没有匹配A行的B行的行,并且您得到的结果与INNER JOIN的结果相同,因此外部联接已成为内部联接
凯斯·贾德

是的,我已经检查了以下结果:SELECT的funds.id,prospects.id来自funds内部加入的潜在客户(prospects.id = funds.lead_id和prospects.is_manual ='no')和SELECT的fund.id,prospects.id来自funds左侧加入上(prospects.id = funds.lead_id)的前景,其中prospects.is_manual = '不'
罗希特DHIMAN

25

大多数RDBMS产品将相同地优化两个查询。在Peter Gulutzan和Trudy Pelzer撰写的“ SQL Performance Tuning”中,他们测试了多个品牌的RDBMS,但没有发现性能差异。

我更喜欢将联接条件与查询限制条件分开。

如果您OUTER JOIN有时在使用,则必须在join子句中放入条件。


1
我同意您的看法,从语法上讲它更干净,我必须尊重您对这本书的了解和很高的声誉,但是上周我可以想到4个查询,它们的执行计划,CPU时间和逻辑读取时间截然不同我将谓词移到了加入的地方。
2009年

2
您在询问最佳做法。一旦您开始测试特定RDBMS实现的工作原理,其他人就会给出正确的建议:基准测试。
Bill Karwin 09年

12

JOIN发生后,WHERE将会过滤。

在JOIN上进行过滤,以防止在JOIN过程中添加行。


10
从语义上讲,它们在INNER JOIN过程中被阻止,但是优化器可以随意重新排列INNER JOIN和WHERE谓词,因此,如果需要,优化器可以自由地排除它们。
Cade Roux

1
Cade Roux:对。通常,用SQL编写的内容不是说完所有内容后优化器就会为您提供的内容。那么,我想这在全理论的世界中是正确的,而在自动查询优化器的世界中,您的答案当然更正确了:)
TheTXI 2009年

我喜欢这种情况的解释ON
罗伯特·罗查

3

我更喜欢JOIN联接完整的表/视图,然后使用WHERE引入结果集的谓词。

在语法上感觉更干净。


2

我通常会在对联接进行过滤时看到性能提高。特别是如果您可以在两个表的索引列上联接。您也应该能够通过大多数查询来减少逻辑读取,这在大容量环境中是比执行时间更好的性能指标。

当有人展示他们的SQL基准测试,并且他们在午夜在开发服务器上执行了50,000次两个版本的sproc并比较平均时间时,我总是感到很开心。


0

在我看来,将条件置于联接中似乎“严重错误”,因为这不是JOIN的“目的”。但这是非常定性的。

另一个问题:如果您决定从内部联接切换为正确的联接,那么将条件置于JOIN内可能会导致意外结果。


3
有时,这些结果有点“预期”,有时甚至是“有意的”(例如,对于外部联接,其中WHERE条件的语义与JOIN条件的语义不同)。
马塞尔·托斯

0

我认为,当您拥有更大的表时,联接会更快。其实并没有太大的区别,尤其是当您处理的表较小时。当我第一次了解联接时,我被告知联接中的条件就像where子句条件,并且如果where子句特定于要在哪个表上执行条件的条件下,我可以互换使用它们。


-4

最好在Join中添加条件。性能比可读性更重要。对于大型数据集,这很重要。


1
您是否有某种证据,研究上述谓词的位置如何影响性能?
Zso
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.