为啥优化器不使用我的唯一过滤索引?

Posted

技术标签:

【中文标题】为啥优化器不使用我的唯一过滤索引?【英文标题】:Why isn't the optimizer using my unique filtered index?为什么优化器不使用我的唯一过滤索引? 【发布时间】:2014-09-26 16:31:51 【问题描述】:

示例架构:

create table dbo.Person (
    ID int identity(1,1) not null
        constraint PK_Person primary key,
    UserName nvarchar(50) null,
    EncryptedPassword nvarchar(100) null
)
create index IX_Person_Login
    on dbo.Person (UserName, EncryptedPassword)
    include (/* other columns */)
create unique index IX_Person_UserName
    on dbo.Person (UserName)
    where (UserName is not null)

所以现在,如果我按用户名查找 ID,我希望优化器会选择更小、更有选择性的索引。 IX_Person_UserName 也应该被覆盖,因为 ID 是集群键(结果计划确实证明了这一点,但这不是问题的重点)。

select ID
from dbo.Person
where UserName = @UserName
and UserName is not null

然而,优化器选择在 IX_Person_Login 上执行 INDEX SEEK,它不是唯一的,键中有更多列,并且其叶节点更大。如果我强制使用 IX_Person_UserName,估计成本是一样的。在这两种情况下,估计的行数都超过 100,但实际的行数是 1。我尝试更新统计信息,但这对选择的计划或估计的行数也没有任何影响。是因为 SQL Server 的计划考虑了 @UserName 可能为空的可能性吗?即使我在查询中放置了一个文字非空字符串值,它仍然不使用唯一过滤索引。谁能解释这种行为?

【问题讨论】:

尝试将ID包含在索引中 @mxix 没必要,因为是聚集索引 @Lamak,他想要给定 的 ID。由于 IX_Person_Username 不包含 ID,因此不会使用它。如果 IX_Person_Username 包含 ID,优化器可能会为给定的查询选择它。 @mxix 每个非聚集索引都包含聚集索引的键(如果表中有键)。所以,是的,X_Person_Username 确实包含 ID @Lamak 是正确的。当我通过表提示强制使用 IX_Person_UserName 时,查询计划是索引上的简单 INDEX SEEK。如果索引没有覆盖查询,也会有一个 BOOKMARK LOOKUP。 【参考方案1】:

查询优化器不一定选择最优计划。

这是有道理的,因为有时最好按照您现在拥有的好计划运行,而不是浪费大量时间寻找更快的计划。

如果您想强制查询使用该索引,那么您可以使用 TABLE HINT

SELECT Id FROM dbo.Person p 
WHERE UserName is not null
OPTION  (TABLE HINT(p , INDEX (IX_Person_UserName)))

https://msdn.microsoft.com/en-us/library/ms181714.aspx

如果您同时运行两个版本并包含实际执行计划

SELECT Id FROM dbo.Person p 
WHERE UserName is not null
OPTION  (TABLE HINT(p , INDEX (IX_Person_UserName)))

SELECT Id FROM dbo.Person p 
WHERE UserName is not null
OPTION  (TABLE HINT(p , INDEX (IX_Person_Login)))

您将能够从相对于批次的查询成本中看到它是否真的有任何区别。

我希望你会看到

Query1: Query cost (relative to the batch): 50%

Query2: Query cost (relative to the batch): 50%

【讨论】:

感谢提醒,有时优化器会选择“足够好”而不是“由我确定的最佳”

以上是关于为啥优化器不使用我的唯一过滤索引?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 优化器不接受索引提示

为啥 mysql 优化器没有使用完整的索引?

SQL,未使用的 LEFT JOIN 速度变慢,优化器不起作用?

MySQL 优化器 - 成本规划器不知道 DuplicateWeedout 策略何时创建磁盘表

为啥查询优化器完全忽略索引视图索引?

优化器忽略过滤的索引条件