sql - 两个实例中相同数据库上的执行计划不同
问题描述
我正在为一个神秘的问题而苦苦挣扎:我在生产中遇到了一个查询,它使他的性能急剧下降,从几秒钟到两分钟。通过分析执行计划,我发现它莫名其妙地不再对表中的某个字段使用非集群索引。我执行了以下所有步骤:
- 检查索引碎片:
- 重新编译索引;
- 丢弃的统计数据;
- 重新创建索引;
- 重新启动服务器;但是什么都没有。每当我启动查询时,执行计划都不使用索引。
如果我强制查询使用索引(带有查询提示),它会立即完成。为什么sql server在执行计划中不考虑索引?
为了尝试重现问题,我在另一个 sql server 实例中恢复了相同的 db:结果是执行计划默认使用索引。
为什么在生产中仍然不使用索引?(硬件配置很好,服务器license是标准版)
查询是:
SELECT Anagrafiche.[Ragione Sociale], Anagrafiche.IDAnagrafica
FROM ([Registrazioni Magazzino]
INNER JOIN Anagrafiche ON [Registrazioni Magazzino].IDCliente = Anagrafiche.IDAnagrafica)
INNER JOIN Movimenti ON [Registrazioni Magazzino].RifIDMovimenti = Movimenti.ID
GROUP BY Anagrafiche.[Ragione Sociale], Anagrafiche.IDAnagrafica, Anagrafiche.Fornitore
HAVING (((Anagrafiche.Fornitore)=1))
ORDER BY Anagrafiche.[Ragione Sociale]
正确的执行计划是: 对
错误是: 错
表 [registrazioni magazzino] 包含大约 980.000 行。字段为 [idcliente],索引为 [IX_Registrazioni Magazzino_2]
解决方案
上的非聚集索引[Registrazioni Magazzino]
不是覆盖索引,换句话说:仍有一些列需要通过键查找从聚集索引中取出。
因此编译器需要根据统计数据来决定是否使用索引并进行键查找;或者,考虑到行数,这些额外的键查找的成本是否不值得,而是应该对聚集索引进行扫描。
更复杂的是,由于实际上没有从 中选择任何列[Registrazioni Magazzino]
,因此查询已转换为EXISTS
查询,这本来应该是这样编写的。1 的 row-goal/TOP
放置在连接的内侧,因此编译器认为它会很快得到结果,而忽略了进行如此多的扫描的巨大成本。
这里的解决方案很简单:将非聚集索引改为包含RifIDMovimenti
列。您可以将其作为第二个键列执行,但我认为INCLUDE
列会更好:
CREATE NONCLUSTERED INDEX [IX_Registrazioni Magazzino] ON [Registrazioni Magazzino]
(IDCliente) INCLUDE (RifIDMovimenti)
WITH (DROP_EXISTING = ON);
上的索引Anagrafiche (Fornitore, [Ragione Sociale]) INCLUDE (IDAnagrafica)
也会有所帮助。这意味着Fornitore
可以预先过滤,并且排序将从查询中删除。
关于您的查询的进一步说明:
- 您应该使用别名,尤其是当您的表名带有空格时(无论如何都应该避免使用)
- 如前所述,这实际上应该是一个
EXISTS
查询,没有GROUP BY
- 简单的非聚合过滤器应该放在
WHERE
不HAVING
- 不需要括号连接,它不会帮助或改变编译器所做的事情
这些更改不是必需的,但有助于查询理解,也可能有助于优化器。
因此,您的查询理想情况下会变成这样:
SELECT a.[Ragione Sociale], a.IDAnagrafica
FROM Anagrafiche a
WHERE a.Fornitore = 1
AND EXISTS (SELECT 1
FROM [Registrazioni Magazzino] r
INNER JOIN Movimenti m ON r.RifIDMovimenti = m.ID
WHERE r.IDCliente = a.IDAnagrafica)
ORDER BY a.[Ragione Sociale];
推荐阅读
- python - 当键不统一时,如何在不使用 Pandas 的情况下从嵌套字典中绘制条形图?
- woocommerce - WooCommerce:合并追加销售和交叉销售
- ios - 如何在模拟器上安装 iOS 应用发布版本
- php - SQL 查询返回一行太少
- html - 多重角色="radiogroup"
- vue.js - 如何在子组件上调用 submit.prevent
- c++ - 在 C++ 中显示四舍五入的值而不是十进制的精确值
- vuejs3 - 如何在 vuejs3 中编译 vue 模板
- android - 将 ActionBar 按钮的可见性设置为 GONE
- php - PHP 标头位置在某些情况下不起作用