免责声明:这不是一个如何改善效果的问题,而是为什么它首先是不好的。
以下查询实际上是一些较大查询的本质,但小到足以证明我不理解的问题。
所涉及的表是(跳过列 - 我希望 - 无关紧要):
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。这显然需要两分钟才能完成。虽然我理解这个数字的来源(这是20182的29187倍,而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
的{{1}}上创建其他索引。
在 加入上使用StanyJednostek
提示会导致以下执行计划:
答案 0 :(得分:6)
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可能认为因为它在PK中,所以它必须是唯一的。这可能是查询优化程序中的一个错误。
为什么SQL Server提升DISTINCT
运算符?从猜测中可以看出,DISTINCT
运算符将应用于20,802行,之前或之后加入 - 没有区别!所以我猜他只选了一个。
一些优化建议:
根本不需要SELECT DISTINCT IndeksID
子查询!这可能会带来性能的最大改善。
如果你真的坚持保留SELECT DISTINCT
由于某些原因不在这个问题中,我建议将其实现为临时表。它强制SQL Server在较小的行集上应用DISTINCT
(29,187)
您可以通过在查询末尾添加OPTION (FORCE ORDER)
来强制加入订单。但要小心谨慎地使用它。
您可以通过INNER HASH JOIN
强制加入,但请再次注意不会立即显示的不良效果。任何类型的查询提示都有风险。
答案 1 :(得分:1)
第二个内部联接会扩展行数,因为StanyJednostek.JednostkaID = Jednostki.JednostkaID
是N:1。这会将散列连接所需的内存增加到可用系统之上,因此无法使用散列连接。
至于为什么丢失的索引提示消失了:因为散列连接不需要它。丢失的指数很可能会改善性能。