未使用计算列索引

Dav*_*vre 14 sql-server index-tuning computed-column

我想根据两列是否相等进行快速查找。我尝试使用带有索引的计算列,但 SQL Server 似乎没有使用它。如果我只使用带有索引的静态填充位列,我会得到预期的索引查找。

似乎还有其他一些类似的问题,但没有一个关注为什么不使用索引。

测试表:

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    )

create index ix_DiffPersisted on Diffs (DiffPersisted)
create index ix_DiffComp on Diffs (DiffComp)
create index ix_DiffStatic on Diffs (DiffStatic)
Run Code Online (Sandbox Code Playgroud)

和查询:

select Id from Diffs where DiffPersisted = 1
select Id from Diffs where DiffComp = 1
select Id from Diffs where DiffStatic = 1
Run Code Online (Sandbox Code Playgroud)

以及由此产生的执行计划: 执行计划

Aar*_*and 10

尝试使用COALESCE而不是ISNULL. 使用ISNULL,SQL Server 似乎无法针对更窄的索引推送谓词,因此必须扫描聚集的信息才能找到信息。

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    );
Run Code Online (Sandbox Code Playgroud)

也就是说,如果您坚持使用静态列,过滤索引可能更有意义,并且 I/O 成本更低(这一切都取决于通常与过滤谓词匹配的行数),例如:

CREATE INDEX ix_DiffStaticFiltered 
  ON dbo.Diffs(DiffStatic)
  WHERE DiffStatic = 1;
Run Code Online (Sandbox Code Playgroud)


Pau*_*ite 5

这是 SQL Server 计算列匹配逻辑的特定限制,当使用最外层ISNULL且列的数据类型为bit.

错误报告

为避免该问题,可以采用以下任何一种变通方法:

  1. 不要使用最外层ISNULL(制作计算列的唯一方法NOT NULL)。
  2. 不要使用bit数据类型作为计算列的最终类型。
  3. 制作计算列PERSISTED启用跟踪标志 176

细节

问题的核心是,如果没有跟踪标志 176,查询中的所有计算列引用(甚至是持久化的)总是在查询编译的早期扩展到底层定义中。

扩展的想法是它可以实现只能对定义起作用的简化和重写,而不能仅对列名起作用。例如,查询中可能存在引用该计算列的谓词,这可能会使部分计算变得多余,或者受到更多限制。

一旦考虑了早期的简化和重写,查询编译就会尝试将查询中的表达式与计算列(所有计算列,而不仅仅是那些最初在查询文本中找到的列)进行匹配。

在大多数情况下,未更改的计算列表达式匹配回原始计算列没有问题。当特定于匹配bit类型表达式时,似乎存在一个错误,最外面的ISNULL. 在这种特定情况下,匹配是不成功的,即使对内部结构的详细检查表明它应该成功。