Sql server WHERE子句最好在IN和JOIN之前或之后执行
我读了这篇文章:Sql server WHERE子句最好在IN和JOIN之前或之后执行,sql-server,Sql Server,我读了这篇文章: 在文章末尾已被写入并加入条款考虑在哪里。 假设我们有一个记录了1000万条记录的主表和一个记录了5000万条记录的细节表(引用了主表(FK))。我们有一个查询,根据主表中的PK,不只是100条细节表记录 在这种情况下,加入和执行在哪里?我的意思是,我们在加入之后有500毫秒记录,然后在哪里应用它呢?还是先申请,然后加入,再考虑?如果第二个答案是真的,它与顶部文章有不一致性吗? 谢谢我想你把上的误读为中的 但是,文章中显示的顺序是正确的(显然它是msdn)。上的和连接在其中之前
在文章末尾已被写入并加入条款考虑在哪里。
假设我们有一个记录了1000万条记录的主表和一个记录了5000万条记录的细节表(引用了主表(FK))。我们有一个查询,根据主表中的PK,不只是100条细节表记录在这种情况下,加入和执行在哪里?我的意思是,我们在加入之后有500毫秒记录,然后在哪里应用它呢?还是先申请,然后加入,再考虑?如果第二个答案是真的,它与顶部文章有不一致性吗?
谢谢我想你把上的误读为中的 但是,文章中显示的顺序是正确的(显然它是msdn)。
上的和连接
在其中
之前自然执行,因为其中
必须作为过滤器应用于由于连接而获得的临时结果集
这篇文章只是说这是执行的逻辑顺序,而且在这一段的末尾也添加了这一行;)
“请注意,语句的实际物理执行由查询处理器决定,顺序可能与此列表不同。”没有定义的顺序。SQL引擎根据优化器选择的执行策略确定执行操作的顺序。这无关紧要
始终遵守逻辑处理顺序:无论实际处理顺序如何
内部连接和WHERE条件是有效的关联和交换的(因此ANSI-89“在WHERE中连接”语法),因此实际顺序并不重要
对于外部联接和更复杂的查询,逻辑顺序变得非常重要:在外部表上应用WHERE将完全改变逻辑
同样,只要查询语义是按照逻辑处理顺序维护的,那么优化程序在内部如何进行并不重要
这里的关键词是“optimizer”:它完全按照它所说的去做在一个内部联接或一个左联接中左边的表的情况下,在许多情况下,优化器会发现最好先执行任何过滤(最高选择性)在实际执行任何类型的物理连接之前-显然有更好的物理操作顺序
在某种程度上,您有时可以通过SQL控制(或干预),例如,通过子查询中的聚合
处理查询中约束的逻辑顺序只能根据已知的不变变换进行变换
因此:
在逻辑上仍然等同于:
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
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
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
AND a.something = something
AND b.something = something
因此优化器不会将它们转换为相同的执行计划
优化器非常智能,能够非常成功地移动对象,包括折叠视图和内联表值函数,甚至可以非常成功地通过某些聚合向下推送对象
通常,当您编写SQL时,它需要是可理解的、可维护的和正确的。就执行效率而言,如果优化器很难将声明性SQL转换为具有可接受性能的执行计划,代码有时可以简化,或者添加适当的索引或提示,或者分解成执行速度更快的步骤——所有步骤都是以连续的入侵顺序进行的。只要重新阅读Paul White的并记住这个问题
可以使用未记录的命令禁用特定的转换规则,并深入了解应用的转换
出于(希望如此!)明显的原因,请仅在开发实例上尝试此操作,并记住重新启用它们,并从缓存中删除任何不理想的计划。
使用AdventureWorks2008;
/*禁用规则*/
DBCC规则关闭(“SELonJN”);
DBCC规则关闭(“构建假脱机”);
选择P.ProductNumber,
P.ProductID,
一、数量
来自生产。产品P
加入Product.ProductInventory I
关于I.ProductID=P.ProductID
其中I.ProductID<3
选项(重新编译)
您可以看到,禁用这两个规则后,它会进行笛卡尔连接并在之后进行过滤
/*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)
/*重新启用它们*/
DBCC规则(“SELonJN”);
DBCC规则(“构建假脱机”);
选择P.ProductNumber,
P.ProductID,
一、数量
来自生产。产品P
加入Product.ProductInventory I
关于I.ProductID=P.ProductID
其中I.ProductID<3
选项(重新编译)
启用它们后,谓词将被直接下推到索引查找中,从而减少联接操作处理的行数
关键词是“逻辑”。这不是SQL Server实际所做的!您的链接上写着“请注意,语句的实际物理执行由查询处理器决定,顺序可能与此列表不同。”有一个逻辑顺序是值得遵守的,无论实际使用的顺序是什么。我很难理解您的左连接
示例。为什么它不是等价的?WHERE
是否适用于联接结果?@Toskan在后者中,如果a.id=b.id
,a.something=something
或b.something=something
计算为false,则返回一行b
字段的空值;在前者中,如果a.something=something
或b.something=something
的计算结果为false,则不会返回一行。@MattArnold,谢谢。因此,简而言之:只要a.id=b.id上有行,左连接b将始终返回一些内容。基本上,在a.id=b.id上左连接b
就像在说“如果您与b.id
匹配就好了,但是如果您不能