[英]Why is SQL Server choosing the “wrong” Index?
我有一个包含大约 2 亿条记录的事务表,一个主键聚集在 Id 和 2 个索引上:
在继续执行实际查询以更新统计信息之前,我会运行这两条语句
Update STATISTICS dbo.[Transaction] IX_SiloId_ChangedTime_IncludeTime WITH FULLSCAN
Update STATISTICS dbo.[Transaction] IX_SiloId_Time_IncludeContent WITH FULLSCAN
这是我的查询:
DECLARE @Query SiloTimeQueryTableType -- (SiloId, Time) with primary key clustered on SiloId
INSERT INTO @Query VALUES
(1, '2020-12-31'), -- 1000 total values, though it's still the same problem with just one
SELECT t.*
FROM [Transaction] t
INNER JOIN @Query q
ON t.SiloId = q.SiloId
WHERE
t.Time >= q.Time
现在发生的事情是无论出于何种原因 Sql 服务器选择IX_SiloId_ChangedTime_IncludeTime
。 然后它需要永远。 如果我使用WITH (INDEX(IX_SiloId_Time_IncludeContent))
我会立即得到结果。
正确的索引在这里很明显,但是 SQL 服务器选择了甚至没有按时间索引的那个。
我无法理解这种行为,但从我阅读的内容来看,最好避免对索引的提示,尽管我在制作这个索引时考虑到了这个查询。
所以问题是:我该怎么做才能弄清楚为什么 SQL 服务器更喜欢“错误”索引,即使存在更好的索引并且我只是运行完整的统计更新?
我创建了一个临时表,因为许多人认为 TVP 失败,但结果是一样的:
CREATE TABLE #Query
(
SiloId bigint NOT NULL PRIMARY KEY CLUSTERED,
Time datetime2(7) NOT NULL
)
执行计划:
https://www.brentozar.com/pastetheplan/?id=rJOt3G00P
https://www.brentozar.com/pastetheplan/?id=ByFshGAAP (这个是实时的,因为它需要太长时间)
指数:
CREATE NONCLUSTERED INDEX [IX_SiloId_Time_IncludeContent] ON [dbo].[Transaction]
(
[SiloId] ASC,
[Time] ASC
)
INCLUDE([SiloContent]) WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_SiloId_ChangedTime_IncludeTime] ON [dbo].[Transaction]
(
[SiloId] ASC,
[ChangedTime] ASC
)
INCLUDE([Time]) WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
无论出于何种原因 Sql 服务器选择 IX_SiloId_ChangedTime_IncludeTime
这不是执行计划所说的。 SQL 服务器在未指定索引提示时选择PK_Transaction
聚集索引。
我很清楚为什么 SQL 服务器在查看执行计划时选择PK_Transaction
而不是IX_SiloId_Time_IncludeContent
。 原因是基数估计差。 两个执行计划都显示 SQL Server 估计连接操作会产生 2.5182.000 行,但它实际上会产生 4.155 行。 如果 SQL 服务器选择IX_SiloId_Time_IncludeContent
那么它估计它需要执行 2.5182.000 键查找。 使用IX_SiloId_Time_IncludeContent
索引进行 2.5182.000 键查找时,该计划比使用 hash 匹配和聚集索引扫描的计划更昂贵。 如果 SQL 服务器能够更好地估计,它会选择IX_SiloId_Time_IncludeContent
因为只有 4.155 个键查找,该计划的成本要低得多。
所以,你可以做什么?。 我认为有两个选择:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.