WHERE X <> 1 AND Y <> 1 ORDER BY Z 的正确索引

Phi*_*ler 3 index sql-server-2008 sql-server

我有一个相对较大的表,它以一种方式访问​​,正好通过一个查询(在读取端)。

查询按两列(均为bit)过滤表,并按第三列(整数)排序​​。请注意,查询命名列(它实际上不使用“*”)。“TOP 1000”是有意的,是生产查询的一部分:

生成了查询——不知道为什么它会以这种方式生成。

SELECT TOP 1000
     *
FROM
     MyTable m
WHERE
     m.IsFlag1 != 1
     AND m.IsFlag2 != 1
ORDER BY
     m.SomeId
Run Code Online (Sandbox Code Playgroud)

我有一个覆盖索引,主列是IsFlag1IsFlag2

执行计划显示并在此索引上查找索引,然后进行排序。根据执行计划,排序占查询成本的 97%。

我尝试添加SomeId为索引的主列(第三列),但执行计划保持不变。

然后我尝试添加一个仅使用 SomeId 作为主列的覆盖索引,保留原始索引。执行计划然后对新查询进行索引扫描,操作成本显着降低(并且在功能上更快)。

说了这么多,我想尽可能地优化这个查询。有没有办法修改索引,以便它简单地进行查找?

Mar*_*ith 6

一般来说,不可能在条件x <> 1和上使用索引查找y <> 1

使用x,y最好的索引,您可以将其转换为两个范围搜索(x < 1x > 1),并 带有剩余谓词y <> 1(并且这将无法使用额外的索引键列来避免排序)

对于一bit列,因为它只能有三个值。0, 1,NULL逻辑上WHERE bit_column <> 1等同于 WHERE bit_column = 0但似乎 SQL Server 没有利用这里的优势并为您转换<>=条件。

添加几个检查约束可以完成这项工作,尽管这些显然是多余的,因为它们实际上并没有以任何方式限制数据类型的允许值(因为NULL如果检查约束UNKNOWN计算为通过)

CREATE TABLE MyTable
  (
     Foo     INT,
     IsFlag1 BIT NULL CHECK (IsFlag1 IN (0, 1)),
     IsFlag2 BIT NULL CHECK (IsFlag2 IN (0, 1)),
     SomeId  INT
  );

CREATE NONCLUSTERED INDEX ix
  ON MyTable(IsFlag1, IsFlag2, SomeId)
  INCLUDE (Foo); 
Run Code Online (Sandbox Code Playgroud)

该计划现在确实显示了寻求 IsFlag1 = 0 AND IsFlag2 = 0

计划一

或者,这个过滤索引也避免了需要一个 SORT

CREATE NONCLUSTERED INDEX ix 
           ON MyTable(SomeId) 
           INCLUDE (Foo,IsFlag1, IsFlag2) 
           WHERE IsFlag1 != 1 and IsFlag2 != 1
Run Code Online (Sandbox Code Playgroud)

它对筛选的索引(按 排序的合格行SomeId)进行TOP扫描,并在检索到 1,000 行后停止扫描。在索引IsFlag1, IsFlag2中使用INCLUDE-d 以避免在没有这个的情况下发生不必要的查找

计划二