免责声明:这不是一个如何提高性能的问题,而是为什么它首先是不好的。
以下查询实际上是一些更大查询的本质,但小到足以说明我不明白的问题。
所涉及的表格是(跳过那些 - 我希望 - 不相关的列):
create table StanyJednostek (JednostkaID nchar(5), IndeksID nchar(18),
primary key (JednostkaID, IndeksID))
create table Jednostki (JednostkaID nchar(5),
primary key (JednostkaID))
StanyJednostek
包含 29187 行,而有 1676 个不同的 IndeksID
此表中的值)。 Jednostki
包含 94 行。现在,这个查询需要两分钟才能完成:
select
StanyJednostek.JednostkaID, StanyJednostek.IndeksID
from StanyJednostek
inner join
(select distinct IndeksID from StanyJednostek) as Zmiany
on StanyJednostek.IndeksID = Zmiany.IndeksID
inner join
Jednostki on StanyJednostek.JednostkaID = Jednostki.JednostkaID
下面是执行计划:
困扰我的是大量实际行:607147974。这显然需要两分钟才能完成。虽然我知道这个数字来自哪里(这是 29187 乘以 20802,而 20802 是
StanyJednostek
和 Jednostki
之间的成功连接数),但我不太明白为什么查询优化器决定在这里选择嵌套循环?为什么不是Zmiany
某种迭代的临时集而不是整个源表?同样有趣的是,虽然查询的最后两行似乎无关紧要,但如果我删除这些行,执行计划更改和嵌套循环将替换为散列:select
StanyJednostek.JednostkaID, StanyJednostek.IndeksID
from StanyJednostek
inner join
(select distinct IndeksID from StanyJednostek) as Zmiany
on StanyJednostek.IndeksID = Zmiany.IndeksID
请注意,查询优化器也不再建议在
IndeksID
上创建额外的索引。在 StanyJednostek
.使用
HASH
对任一连接的提示都会导致以下执行计划:最佳答案
SQL Server 将连接重新排序为它认为最有效的方式。在这种情况下,它猜错了。请注意,您的第一个执行计划的连接顺序如下:
StanyJednostek
INNER JOIN Jednostki
INNER JOIN (SELECT DISTINCT IndeksID FROM StanyJednostek)
第一次加入几乎没什么可写的 - 29187 到 94 行不是问题。但是查询优化器对这个连接的结果集猜错了。它认为这个临时结果集只有 1 行。
因此,它选择了一个嵌套循环并认为它会扫描
StanyJednostek
只有一个(估计执行次数 = 1)。实际上,它会扫描 StanyJednostek
20,802 次(第一个结果集中的行数,请参阅执行次数)。请注意
DISTINCT
运营商还没有找到。它在两个连接都执行后应用。当然,到那时您将处理 607,147,974 行。自
IndeksID
是复合主键的一部分(也不是最左边的键),SQL Server 不会单独保留详细的统计信息。因此,指数建议。编辑:
JednostkaID
.查看该列如何出现在两个表的 PK 中。 SQL Server 可能会认为,因为它在 PKs 中,所以它必须是唯一的。这可能是查询优化器中的一个错误。 DISTINCT
运算符(operator)? 从它的猜测中,它看到DISTINCT
运算符将应用于 20,802 行,在连接之前或之后 - 没有区别!所以我的猜测是它只选择一个。 一些优化建议:
SELECT DISTINCT IndeksID
根本不需要子查询!这可能会带来最大的性能提升。 SELECT DISTINCT
由于某些原因不在此问题中,我建议将其具体化为临时表。它强制 SQL Server 应用 DISTINCT
在较小的一组行上 (29,187) OPTION (FORCE ORDER)
来强制加入顺序到查询结束。但是要小心谨慎地使用它。 INNER HASH JOIN
强制已加入,但同样,请注意无法立即看到的不良影响。任何类型的查询提示都有风险。 关于sql - 为什么选择嵌套循环会导致 "self join"的执行时间过长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28441468/