【问题标题】:Condition within JOIN or WHEREJOIN 或 WHERE 中的条件
【发布时间】:2010-11-04 08:57:43
【问题描述】:

将条件放入 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'

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

【问题讨论】:

  • 您是否运行了这两个查询?您是否检查了两个查询生成的执行计划?你观察到了什么?
  • @S.Lott,此查询仅用于示例目的。我只是想知道“一般”这是首选方法 - 如果有的话。
  • @Steve Dignan:您应该使用示例数据对此进行基准测试并查看查询计划。答案会非常非常清楚。而且 - 奖励 - 当出现更复杂的情况时,您将拥有一段可以重用的代码。
  • 如果条件描述了关系,我会亲自将条件放在 JOIN 子句中。仅过滤结果集的通用条件将转到 WHERE 部分。例如。 FROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
  • 问题和解决方案专门针对 INNER JOIN。如果连接是 LEFT/RIGHT/FULL OUTER JOIN,那么这不是偏好或性能问题,而是正确结果之一。 SQL Cookbook(第 11.3. 使用外部联接时合并 OR 逻辑)演示了联接和 where 条件之间的区别。

标签: sql performance


【解决方案1】:

关系代数允许WHERE 子句和INNER JOIN 中的谓词可互换,因此即使是带有WHERE 子句的INNER JOIN 查询也可以让优化器重新排列谓词,以便它们可能已经在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

但这当然取决于。

【讨论】:

  • 这不仅关乎干净的查询或可读性,还关乎性能。将条件放入连接可提高具有正确索引表的大量数据的性能。
  • 我只是运行月度销售报告,加入数百万条记录的 5-6 个表。性能提高 30% - sql server 2012
  • @Shahdat 如果您在将过滤条件从 where 子句移动到内部联接时获得了显着的性能差异,您需要发布这些执行计划。
  • @Cade 我已经调查了执行计划——两种情况都显示出相同的成本。我多次运行查询似乎都花费了相同的时间。以前,我在生产环境中运行查询并获得显着的性能差异,因为实时用户正在使用数据库。很抱歉造成混乱。
  • 这个答案适用于 INNER JOIN,但不适用于左/右连接。
【解决方案2】:

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

但是,如果您使用左连接或右连接,则放置条件的位置会有很大的不同。例如考虑以下两个查询:

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

【讨论】:

  • 感谢您举例说明
  • “从而将左连接转换为内连接”。如何?你能详细说明一下吗?
  • @user1451111 了解 LEFT/RIGHT JOIN 返回的内容:INNER JOIN 行加上由 NULL 扩展的不匹配的左/右表行。 FULL JOIN 返回由 NULL 扩展的 INNER JOIN 行 UNION ALL 不匹配的左右表行。作为 OUTER JOIN 的一部分,始终知道您想要什么 INNER JOIN。 WHERE 或 ON 在 OUTER JOIN ON 删除任何由 NULL 扩展的行后,要求可能为 NULL 扩展的列不为 NULL,即只留下 INNER JOIN 行,即“将 OUTER JOIN 转换为 INNER JOIN”。
  • @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 fund.id,prospects.id FROM funds 内部加入潜在客户(prospects.id =funds.lead_id 和前景.is_manual='no')并从funds 中选择funds.id、prospects.id 离开加入潜在客户(prospects.id = fund.lead_id),其中prospects.is_manual='no'
【解决方案3】:

大多数 RDBMS 产品都会对这两个查询进行相同的优化。在 Peter Gulutzan 和 Trudy Pelzer 的“SQL 性能调优”中,他们测试了多个品牌的 RDBMS,没有发现性能差异。

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

如果您使用OUTER JOIN,有时需要在连接子句中添加条件。

【讨论】:

  • 我同意你的观点,它在语法上更简洁,我不得不听从你对那本书的了解和你非常高的声誉,但我可以想到上周的 4 个查询,它们的执行计划非常不同、CPU 时间和当我将 where 谓词移动到连接时的逻辑读取。
  • 您在询问最佳实践。一旦您开始测试特定 RDBMS 实现的工作原理,其他人就会给出正确的建议:基准测试。
【解决方案4】:

WHERE 将在 JOIN 发生后过滤。

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

【讨论】:

  • 在语义上,它们在 INNER JOIN 过程中被阻止,但优化器可以随意重新排列 INNER JOIN 和 WHERE 谓词,因此优化器可以在以后随意排除它们。
  • 凯德鲁:对。很多时候,您在 SQL 中编写的内容并不是优化器在说完所有内容后会提供给您的内容。那么我想这在全理论世界中是正确的,而在自动查询优化器的世界中你的答案当然更正确:)
  • 我喜欢ON中对条件的这种解释
【解决方案5】:

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

感觉语法上更干净。

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

      同意投票第二多的答案,即使用LEFT JOINRIGHT JOIN 时会有很大的不同。实际上,下面的两个语句是等价的。所以你可以看到AND 子句在JOIN 之前进行过滤,而WHERE 子句在JOIN 之后进行过滤。

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

      【讨论】:

      【解决方案8】:

      对我来说,将条件放入连接中似乎“语义上错误”,因为这不是 JOIN 的“用途”。但这是非常定性的。

      其他问题:如果您决定从内部联接切换到例如右联接,则将条件放在 JOIN 内可能会导致意外结果。

      【讨论】:

      • 有时这些结果有点“预期”,有时甚至是“有意的”(例如,对于外连接,WHERE 条件与 JOIN 条件的语义不同)。
      【解决方案9】:

      在我看来,当你有一个更大的桌子时,加入会更快。这确实没有太大区别,特别是如果您处理的是一张相当小的桌子。当我第一次了解连接时,有人告诉我,连接中的条件就像 where 子句条件,如果 where 子句特定于在哪个表上执行条件,我可以互换使用它们。

      【讨论】:

        【解决方案10】:

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

        【讨论】:

        • 你有什么证据,研究一下提到的谓词的位置如何影响性能?
        • 并非所有时候性能都优于可读性。可读性是关于与他人分享工作。
        猜你喜欢
        • 1970-01-01
        • 2014-02-07
        • 1970-01-01
        • 1970-01-01
        • 2017-03-20
        • 2019-08-07
        • 2015-05-01
        • 2020-03-16
        相关资源
        最近更新 更多