SQL Server 2016:关联查询执行计划为何未使用Col22索引?
你遇到的这种情况在SQL Server优化器的决策逻辑里其实挺常见的,结合你的表结构和查询语句,咱们来拆解几个可能的原因,以及对应的验证和解决思路:
可能的原因
1. 优化器基于成本选择了更小的索引
你的查询是SELECT count(*),核心需求只是统计符合条件的行数,不需要返回任何具体列值。如果Table2里那个“不属于查询涉及列的其他索引”的物理尺寸(总数据页数)远小于Col22上的索引,SQL Server优化器会认为扫描这个更小的索引的IO成本更低——哪怕它和连接条件无关。毕竟优化器的核心目标是最小化整体执行成本,这种情况下直接扫描小索引,再通过键值匹配连接条件的总成本,可能反而比走Col22索引更低。
2. Col22索引的选择性或成本估算不佳
虽然Col22上创建了索引,但如果它的选择性很差(比如存在大量重复值),或者对应Table1中t1.col12=<value>的匹配行数非常多,优化器会判断使用Col22索引进行嵌套循环或哈希连接的成本更高。举个例子:如果t1.col12=<value>返回了上万条数据,那用Col22索引做上万次查找的成本,可能远超过直接扫描Table2的小索引再做哈希连接的成本。
3. Col22索引碎片严重
如果Col22索引的碎片率很高,会导致索引的IO性能大幅下降。这种情况下,优化器会认为扫描一个碎片化严重的Col22索引,不如扫描另一个更整洁、页数更少的索引划算。
4. 统计信息过时或基数估算偏差
SQL Server 2016的基数估算器虽然比旧版本有改进,但依然可能因为统计信息过时,或者样本量不足,导致对行数的估算出现偏差。比如t1.col12=<value>的实际行数和统计信息里估算的行数差很多时,优化器会错误判断连接后的行数,进而选了不合适的索引。
验证与解决建议
对比索引尺寸和碎片情况
执行下面的查询,看看Col22索引和那个“其他索引”的物理差异:SELECT i.name AS 索引名称, s.total_pages AS 总页数, s.avg_fragmentation_in_percent AS 碎片率 FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Table2'), NULL, NULL, 'DETAILED') s JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id WHERE i.name IN ('Col22索引名', '其他索引名'); -- 替换为实际的索引名称如果那个其他索引的总页数远小于Col22索引,那优化器的选择其实是合理的——扫描更小的索引本来就更快。
强制使用Col22索引验证性能
你可以用索引提示强制优化器使用Col22索引,对比两种执行计划的性能:SELECT count(*) FROM Table1 t1 JOIN Table2 t2 WITH (INDEX(Col22索引名)) -- 替换为Col22索引的实际名称 ON t2.col22 = t1.col11 WHERE t1.col12 = <value>;如果强制使用后性能反而下降,说明优化器的选择是对的;如果性能更好,那大概率是统计信息或基数估算的问题,需要更新统计信息。
更新统计信息
如果怀疑是统计信息的问题,执行下面的语句更新统计信息,然后重新查看执行计划:UPDATE STATISTICS Table1 WITH FULLSCAN; UPDATE STATISTICS Table2 WITH FULLSCAN;调整索引结构(可选)
如果你希望优化器优先使用连接相关的索引,可以考虑为Table2创建一个更紧凑的包含Col22的索引,或者重建Col22索引来消除碎片:-- 重建Col22索引消除碎片 ALTER INDEX Col22索引名 ON Table2 REBUILD;
内容的提问来源于stack exchange,提问作者Elias K

