【问题标题】:WHERE clause better execute before IN and JOIN or afterWHERE 子句在 IN 和 JOIN 之前或之后更好地执行
【发布时间】:2011-03-28 18:03:29
【问题描述】:

我读了这篇文章: Logical Processing Order of the SELECT statement

文末已经写了ON和JOIN子句在WHERE之前考虑。

假设我们有一个包含 1000 万条记录的主表和一个包含 5000 万条记录的明细表(参考主表 (FK))。我们有一个查询,根据 PK 只需要 100 条明细表记录主表。

在这种情况下,ON 和 JOIN 在 WHERE 之前执行?我的意思是,在 JOIN 之后我们有 5 亿条记录,然后在 WHERE 中应用它吗?还是先在 WHERE 应用,然后 JOIN 和 ON 考虑?如果第二个答案是真的,它有吗?与热门文章不一致?

谢谢

【问题讨论】:

  • 关键字是“逻辑”。这不是 SQL Server 实际所做的!您的链接显示“请注意,语句的实际物理执行由查询处理器确定,并且顺序可能与此列表不同。”

标签: sql-server


【解决方案1】:

对于 INNER JOIN 或 LEFT JOIN 中左侧的表,在许多情况下,优化器会发现在实际执行任何类型的物理连接之前最好先执行任何过滤(最高选择性) - 所以显然有更好的物理操作顺序。

在某种程度上,您有时可以通过 SQL 来控制(或干扰)这一点,例如,通过子查询中的聚合。

查询中处理约束的逻辑顺序只能根据已知的不变变换进行变换。

所以:

SELECT *
FROM a
INNER JOIN b
    ON a.id = b.id
WHERE a.something = something
    AND b.something = something

在逻辑上仍然等价于:

SELECT *
FROM a
INNER JOIN b
    ON a.id = b.id
    AND a.something = something
    AND b.something = something

它们通常会有相同的执行计划。

另一方面:

SELECT *
FROM a
LEFT JOIN b
    ON a.id = b.id
WHERE a.something = something
    AND b.something = something

不等于:

SELECT *
FROM a
LEFT JOIN b
    ON a.id = b.id
    AND a.something = something
    AND b.something = something

因此优化器不会将它们转换为相同的执行计划。

优化器非常聪明,能够相当成功地移动事物,包括折叠视图和内联表值函数,甚至可以相当成功地通过某些类型的聚合将事物下推。

通常,当您编写 SQL 时,它需要易于理解、可维护和正确。就执行效率而言,如果优化器难以将声明性 SQL 转换为性能可接受的执行计划,有时可以简化代码或添加适当的索引或提示或将其分解为应该的步骤> 执行得更快——所有这些都是按顺序进行的。

【讨论】:

  • 我无法理解您的 LEFT JOIN 示例。为什么不等价? WHERE 是否适用于连接结果?
  • @Toskan 在后者中,如果 a.id = b.ida.something = somethingb.something = something 评估为 false,则将返回一行,其中 b 字段的值为 NULL;在前者中,如果a.something = somethingb.something = something 评估为false,则不会返回一行。
  • @MattArnold 哦,谢谢。简而言之:只要 a 中有行,left join b ON a.id = b.id 总会返回一些东西。基本上left join b ON a.id = b.id 就像说“如果你匹配b.id 会很好,但如果你不能匹配,只需返回ab 的空虚拟对象,而where 只在@987654340 时返回@ 和 b.id 匹配
  • @Toskan 没错,如果找不到匹配项,b 的所有字段都将显示为 NULL。
  • 对于@Toskan 和可能难以理解LEFT JOIN 示例的未来读者,请参阅SQL JOIN - WHERE clause vs. ON clause
【解决方案2】:

没关系

始终遵守逻辑处理顺序:无论实际处理顺序如何

INNER JOIN 和 WHERE 条件有效地关联和交换(因此 ANSI-89 “在 where 中加入”语法),因此实际顺序无关紧要

对于外部连接和更复杂的查询,逻辑顺序变得很重要:在 OUTER 表上应用 WHERE 会完全改变逻辑。

同样,只要查询语义通过遵循逻辑处理顺序来维护,优化器在内部如何处理并不重要。

这里的关键词是“优化器”:它完全按照它所说的去做

【讨论】:

  • 索引列的顺序呢?您是否建议索引中的订单([WHERE 条件],[JOIN 条件])的性能与([JOIN 条件],[WHERE 条件])相同?
  • @sotn:这取决于每列的选择性。有时,您必须同时尝试两者,看看哪个效果最好。
【解决方案3】:

刚刚重读 Paul White 的 excellent series on the Query Optimiser 并记住了这个问题。

可以使用未记录的命令来禁用特定的转换规则并深入了解应用的转换。

出于(希望如此!)显而易见的原因,仅在开发实例上尝试此操作并记住重新启用它们并从缓存中删除任何次优计划。

USE AdventureWorks2008;

/*Disable the rules*/
DBCC RULEOFF ('SELonJN');
DBCC RULEOFF ('BuildSpool');


 SELECT  P.ProductNumber, 
         P.ProductID, 
        I.Quantity
 FROM    Production.Product P
 JOIN    Production.ProductInventory I
         ON  I.ProductID = P.ProductID
WHERE I.ProductID < 3
OPTION (RECOMPILE)

您可以看到禁用这两个规则后,它会进行笛卡尔连接和过滤。

/*Re-enable them*/   
DBCC RULEON ('SELonJN');
DBCC RULEON ('BuildSpool');

 SELECT  P.ProductNumber, 
         P.ProductID, 
        I.Quantity
 FROM    Production.Product P
 JOIN    Production.ProductInventory I
         ON  I.ProductID = P.ProductID
WHERE I.ProductID < 3
OPTION (RECOMPILE)

启用它们后,谓词会被向下推送到索引查找中,从而减少连接操作处理的行数。

【讨论】:

【解决方案4】:

没有明确的顺序。 SQL 引擎根据其优化器选择的执行策略确定执行操作的顺序。

【讨论】:

  • 有一个值得尊重的逻辑顺序,无论在实践中实际使用什么顺序
【解决方案5】:

我认为您将文章中的ON 误读为IN

但是,它在文章中显示的顺序是正确的(显然无论如何都是msdn)。 ONJOIN 自然会在 WHERE 之前执行,因为 WHERE 必须作为过滤器应用于由于 JOINS 而获得的临时结果集

文章只是说这是执行的逻辑顺序,并且在段落的末尾也添加了这一行;)

“请注意,语句的实际物理执行由查询处理器确定,顺序可能与此列表不同。”

【讨论】:

    猜你喜欢
    • 2013-05-14
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多