SQL 服务器中的索引使用情况:搜索与扫描

CASE WHEN index usage in SQL Server: seek vs scan

给定以下架构:

create table dbo.SomeTable 
(
    ID int primary key identity(1, 1),
    SomeColumn varchar(50)
);

create index IX_SomeColumn on dbo.SomeTable (SomeColumn);

并用一些数据填充它:

declare @i int = 1000;

while @i > 0 begin
    set @i = @i - 1;

    insert dbo.SomeTable (SomeColumn) 
    values (convert(varchar, @i))
end

此查询执行索引查找:

select ID 
from dbo.SomeTable 
where SomeColumn = '431'

虽然此查询执行索引扫描:

select ID 
from dbo.SomeTable 
where case when SomeColumn = '431' then 1 else 0 end = 1

有没有办法让后者(或类似的东西)执行索引查找?

我问是因为我希望能够将 case when 放入视图的 select 列表中并在 where 子句中使用它,但它永远不会如果我无法让 SQL 服务器执行索引查找,则性能与原始表单一样好。

如果您查看这两个查询的执行计划,您会发现查找谓词非常不同。

当您不在 where 子句中使用 CASE 表达式时,seek 谓词将单独保留 Column(不必对列值进行任何计算)并且仅在索引上进行 seek 以查找值另一边的=

另一方面,当您在 where 子句中使用 CASE 表达式时,情况发生了很大变化,现在 seek 谓词已将 Where 表达式两侧的 where 子句参数化。简单来说,SQL 服务器不会为 CASE WHEN [TEST_DB].[dbo].[SomeTable].[SomeColumn] = [@1] THEN [@2] ELSE [@3] END = CONVERT_IMPLICIT(int,[@4],0) 产生什么值,直到它被实际执行,因此使用可用索引选项超出了 window 和 SQL服务器最终进行扫描。

故事的寓意

避免在 where 子句中使用 CASE 表达式。

获得搜索的唯一方法是使 case when SomeColumn = '431' then 1 else 0 end 表达式成为计算列并索引计算列。

然后您应该会发现表达式与允许查找的计算列匹配(以维护额外索引为代价)。

(如果您遇到自动参数化问题阻止匹配添加冗余 1=1 将阻止这种情况。SQL Fiddle with plan showing a seek