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)
给定以下架构:
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)