未使用计算列索引


14

我想根据两列是否相等来进行快速查找。我试图使用带有索引的计算列,但是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)

和查询:

select Id from Diffs where DiffPersisted = 1
select Id from Diffs where DiffComp = 1
select Id from Diffs where DiffStatic = 1

以及执行计划: 执行计划

Answers:


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)
    );

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

CREATE INDEX ix_DiffStaticFiltered 
  ON dbo.Diffs(DiffStatic)
  WHERE DiffStatic = 1;

非常有趣,不会想到这一点。看来您现在可以摆脱了COALESCE;我相信该CASE语句已经保证可以返回01,但是ISNULL它只存在,因此SQL Server将为BIT计算列生成非空值。但是,COALESCE仍将产生可为空的列。因此,无论是否带有,此更改的影响之一COALESCE是计算列现在可以为空,但可以使用索引查找。
杰夫·帕特森

@Geoff是的,是的。但是在这种情况下,由于我们通过计算的列定义知道NULL确实不是可能的输出,因此只有当我们将此表用作SELECT INTO的源时,这才真正重要。
亚伦·伯特兰

这是一些了不起的信息-谢谢!我的最终目标是将DataA和DataB列用作“脏” uuid,以允许异步更新记录上的非规范化列,因此Diff标志为1的地方不应过多。字段,那么我正在考虑添加一个触发器来监视两个uuid并更新该字段。
David Faivre 2015年

另外,正如@GeoffPatterson指出的那样,我不能使用COALESCE吗?我为什么要保留它?
David Faivre 2015年

@David您可以放下COALESCE。我试图保持原始代码的外观和意图,并且没有它就没有进行测试,因此测试将由您来进行。(我也无法解释为什么您会首先ISNULL出现在这里。)
亚伦·伯特兰

5

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

错误报告

为避免此问题,可以采用以下任何解决方法:

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

细节

问题的核心在于,没有跟踪标记174,查询中所有计算的列引用(即使是持久的)也总是在查询编译的早期就扩展为基础定义。

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

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

在大多数情况下,未更改的计算列表达式会匹配回原始的计算列而不会出现问题。特定于bit类型匹配最外层类型的表达式时,似乎存在一个错误ISNULL。在这种特定情况下,即使对内部结构进行了详细检查表明匹配应该成功,匹配也不成功。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.