问题:

  我在我的应用程序中使用简单的查询/存储过程访问一个很大的表。但执行了很长时间。在where子句中,我使用了有索引并且高选择性(selective)并且没有用函数包裹的字段。但是看起来像没有使用索引一样,问题出在那里?

  解决方案:

  出现这种微秒的问题原因可能是作为参数的数据类型与查询中的数据类型不一致。在这种情况下,SQLServer将会要么把where中的列,要么把参数的数据类型隐式转换为更高级或者更低级的数据类型。当作为被查询列被转换时(转换竞争中的牺牲者),将引起扫描(scan)来满足查询请求。让我们看看以下两个例子,第一个例子使用示例数据库AdventureWorks,我们将通过一个客户的AccountNumber在Sales.Customer表中查询这个客户。AccountNumber这一列的数据类型是varchar(10)并且上面有一个索引。运行下面的查询并且查看执行计划,可以看到结果如我们所愿:


create proceduredbo.PrecedenceTest
(
 @AccountNumber varchar(10)
)
as
begin
 set nocount on
 select *
 from Sales.Customer
 where AccountNumber = @AccountNumber
end
go
exec dbo.PrecedenceTest'AW00030113'
go


  执行计划如下:

  接着让我们在参数上做些小改动,把它改为nvarchar(10),然后重新执行语句:


alter procedure dbo.PrecedenceTest
(
@AccountNumber nvarchar(10)
)
as
begin
set nocount on
select *
from Sales.Customer
where AccountNumber = @AccountNumber
end
go
exec dbo.PrecedenceTest 'AW00030113'
go


  执行计划显示,优化器选择了扫描TerritoryID上的索引。

  检查Filter操作,可以看到AccountNumber列上被隐式转换了类型来匹配传入的参数。由于数据类型varchar比参数类型nvarchar级别更低,导致其所在的索引失效。