包含或不包含在索引中

Joh*_* N. 2 sql-server index-tuning nonclustered-index sql-server-2016

现在的情况

我正在观察一个简单的语句,该语句正在查询一个表并访问多个索引来检索数据:

SELECT DISTINCT 
feld16,
zahl4,
feld12,
feld19 FROM 
object1 WHERE 
(deleted = 0 or deleted IS NULL)
Run Code Online (Sandbox Code Playgroud)

查询执行计划可以在Brent Ozar 的 Paste The Plan网站上找到,图形表示如下:

上述 SELECT 语句的查询执行计划

该表由包含各种数据的 82 列组成。列中数据的分布deleted为:

 deleted | Number of records
---------+-------------------
       0 |        71'620'068
    NULL |                10
 a value |            59'673
Run Code Online (Sandbox Code Playgroud)

结果集包含大约。大约 6400 万行。7100 万行与搜索谓词匹配WHERE (deleted = 0 or deleted IS NULL)。这是因为DISTINCT遗漏了 700 万条记录。

向前走

为了加快速度,我正在考虑添加一个新索引。最初我以为我有足够的知识来添加足够的索引,但我开始重新审视自己。

问题

以下哪个索引定义(可能)是适当的解决方案?

1.不带INCLUDE的索引

CREATE NONCLUSTERED INDEX [IDXNew] ON [schema_owner].[object1]
(
    [deleted] ASC,
    [feld16] ASC,
    [zahl4] ASC,
    [feld12] ASC,
    [feld19] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
Run Code Online (Sandbox Code Playgroud)

2. 包含索引

CREATE NONCLUSTERED INDEX [IDXNew] ON [schema_owner].[object1]
(
    [feld16] ASC,
    [zahl4] ASC,
    [feld12] ASC,
    [feld19] ASC
)
INCLUDE 
(
    [deleted] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
Run Code Online (Sandbox Code Playgroud)

3.用INCLUDE建立索引,但反之亦然

CREATE NONCLUSTERED INDEX [IDXNew] ON [schema_owner].[object1]
(
    [deleted] ASC
)
INCLUDE(
    [feld12],
    [zahl4],
    [feld16],
    [feld19]
) 
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
Run Code Online (Sandbox Code Playgroud)

我最初的想法

.. 遵循第一个索引定义并包含所有列。在阅读INCLUDE索引创建部分时,我想到创建第二个索引。然后我想:为什么不向索引添加一个过滤器deleted = 0 or deleted IS NULL,然后开始猜测我对索引的了解。

附加信息

SQL Server 2016 实例上运行一些跟踪标志。其中一些是 Microsoft PFE 在 PTOC 期间向我们推荐的。

<TraceFlag Value="2335" Scope="Global" /> -- Assume fixed amount of memory
<TraceFlag Value="2371" Scope="Global" /> -- Updates statistics in linear mode
<TraceFlag Value="4199" Scope="Global" /> -- Enable QO fixes
Run Code Online (Sandbox Code Playgroud)

CE 当前设置为以向后兼容模式运行:

CardinalityEstimationModelVersion="70"
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 6

如果您特别想要优化以快速获得这些结果,那么一种方法是索引视图。

视图定义为

SELECT feld16,
       zahl4,
       feld12,
       feld19,
       COUNT_big(*) AS [count]
FROM   object1
WHERE  ( deleted = 0
          OR deleted IS NULL )
GROUP  BY feld16,
          zahl4,
          feld12,
          feld19 
Run Code Online (Sandbox Code Playgroud)

feld16, zahl4, feld12, feld19然后针对该视图创建的唯一聚集索引SELECT(可能带有NOEXPAND依赖于 SQL Server 版本的提示)可能是获取此数据的最快方法,因为所有数据都将提前预先计算。

在这种情况下,我不愿意使用索引视图,因为它仍然具有与原始表几乎一样多的行。

我会选择您的索引 1 并希望看到两个搜索( fordeleted = 0deleted is null)在其他四列上合并在一起,然后输入到流聚合中以删除重复项,而无需任何排序或散列(或者理想情况下在合并)。

合并联合和流聚合之所以可能,是因为这两种查找都将按顺序返回行[feld16] ASC, [zahl4] ASC, [feld12] ASC, [feld19] ASC

如果您没有获得所需的执行计划,则可能需要重写查询

SELECT feld16,
       zahl4,
       feld12,
       feld19
FROM   object1
WHERE  ( deleted = 0 )
UNION
SELECT feld16,
       zahl4,
       feld12,
       feld19
FROM   object1
WHERE  ( deleted IS NULL ) 
Run Code Online (Sandbox Code Playgroud)

  • 我忘记在测试数据中添加 10 个 NULL 删除行。一旦我这样做了,[旧版](https://www.brentozar.com/pastetheplan/?id=r1mzPw3Fi)和[新CE](https://www.brentozar.com/pastetheplan/?id=rJ-9Dv3Ki )计划是相同的,都使用合并连接。令人惊奇的是,这么小的差异竟然能改变计划的形状。 (3认同)