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

时间:2014-09-26 16:31:52

标签: sql-server

示例架构:

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)

现在,如果我通过UserName查找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可能为null的可能性?即使我在查询中放置了一个文字非空字符串值,它仍然不使用唯一的过滤索引。任何人都可以解释这种行为吗?

1 个答案:

答案 0 :(得分: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%