【问题标题】:CASE WHEN index usage in SQL Server: seek vs scanSQL Server 中的索引使用情况:查找与扫描
【发布时间】:2015-10-20 19:51:50
【问题描述】:

给定以下架构:

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 放入视图的选择列表中并在where 子句中使用它,但如果可以的话,它的性能永远不会像原始表单那样好'不让 SQL Server 进行索引搜索。

【问题讨论】:

  • 您的第二个查询不是 SARGEBLE。在将CASE 语句应用于表中的每一行之前,它无法知道它的值。这就是你得到扫描的原因。当然很明显CASE语句只会在SomeColumn = '431'时返回1,但是SQL Server看起来不够深入,无法弄清楚。

标签: sql-server indexing sql-server-2008-r2 database-performance


【解决方案1】:

您可以在那里进行查找的唯一方法是将case when SomeColumn = '431' then 1 else 0 end 表达式设为计算列并为计算列建立索引。

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

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

【讨论】:

    【解决方案2】:

    如果您查看两个查询的执行计划,搜索谓词会非常不同。

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

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

    故事的寓意

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

    【讨论】:

    • 通过添加两个字面常量的冗余比较可以很容易地防止自动参数化。优化器中没有任何逻辑可以对此类表达式进行逆向工程并尝试使其可搜索。
    • @MartinSmith 哇,这是一个很酷的技巧,刚刚测试过,是的,它确实删除了参数化,但很想知道为什么会出现这种行为?
    • 因为如果它将文字值参数化,可能会阻止其他优化。例如,如果您执行where 1=0,它可以在编译时进行评估,不会返回任何行并完全避免访问表。如果将 1 和 0 替换为参数,则不能这样做。
    猜你喜欢
    • 1970-01-01
    • 2017-10-16
    • 2010-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多