2014-09-26 40 views
0

示例模式:为什么优化器不使用我独特的过滤索引?

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,这不是唯一的,在关键的列多,并且其叶子节点大得多。如果我强制使用IX_Person_UserName,估计的成本是相同的。在这两种情况下,估计的行数都超过了100,但实际的行数是1.我尝试更新统计数据,但是在选择的计划或估计的行数方面没有任何区别。是否因为SQL Server的计划考虑到@ UserName可能为空的可能性?即使我在查询中放置了一个字符串非空字符串值,它仍然不会使用唯一的已过滤索引。谁能解释这种行为?

+0

尝试包括在索引中的ID – mxix 2014-09-26 16:39:02

+0

@mxix没有必要,因为它是聚簇索引 – Lamak 2014-09-26 16:44:47

+0

@Lamak,他想对给定的ID。由于IX_Person_Username不包含该ID,因此不会使用该ID。如果IX_Person_Username包含ID,优化器可能会为给定的查询选择它。 – mxix 2014-09-26 16:51:48

回答

1

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

这很有道理,因为它有时更好地运行在你现在拥有的好计划上,而不是浪费很多时间寻找更快的计划。

如果要强制查询中使用该索引,然后你可以用表提示

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% 
+0

https://msdn.microsoft.com/en-us/library/ms181714.aspx – 2015-12-04 16:46:18

+0

感谢您的提醒,有时优化器会选择“足够好”,而不是“由我决定的最佳” – 2015-12-08 23:49:46