从非聚集索引定义中排除聚集键列

got*_*tqn 3 index sql-server sql-server-2012 sql-server-2014

正如我读过的书签或非聚集索引对表聚集索引的引用是聚集索引键本身。在我参加的SQL 星期六活动中,一位讲师说从非聚集索引键定义和包含子句中排除聚集键列始终是一个好习惯。

我想知道这样做是否有任何负面影响,对于以下情况:

  1. Table A有以下栏目:RecordIDQuestionIDPtsPtsOf和许多其他。
  2. 我创建了一个像这样的非聚集索引:

    CREATE NONCLUSTERED INDEX [IX_TableA_RecordID_QuestionID] ON [dbo].[TableA]
    (
        [RecordID] ASC
       ,[QuestionID] ASC
    )INCLUDE ([Pts],[PtsOf]);
    
    Run Code Online (Sandbox Code Playgroud)

    为了在按记录和问题计算分数时跳过阅读聚集索引。该[RecordID][QuestionID]对是聚集索引键。

如果我像这样更改索引:

CREATE NONCLUSTERED INDEX [IX_TableA_RecordID_QuestionID] ON [dbo].[TableA]
(
    [Pts] ASC
   ,[PtsOf] ASC
);
Run Code Online (Sandbox Code Playgroud)

即使对于不引用点列的查询,索引也会像以前一样使用(它满足搜索条件):

SELECT [RecordID]
      ,[QuestionID]
FROM [dbo].[TableA];

SELECT [RecordID]
FROM [dbo].[TableA];

SELECT [QuestionID]
FROM [dbo].[TableA];
Run Code Online (Sandbox Code Playgroud)

因此,引擎足够聪明,可以看到使用非聚集索引会减少读取次数,我知道将其保留为原始定义也有效,但是有些索引非常大,我认为删除一些键可以减小它们的大小。

我担心的是第一个索引行是按书签排序的,现在不是。我想知道这是否以及如何会损害性能?

ype*_*eᵀᴹ 7

在我参加的 SQL 星期六活动中,一位讲师说从非聚集索引键定义和包含子句中排除聚集键列始终是一个好习惯。

我不同意。让我用你问题中的表格解释一下:

聚集索引是:(RecordID, QuestionID)并且还有更多的列。

任何非聚集索引也将在最后附加聚集键列。因此,索引如下:

(Pts)   is equivalent to:   (Pts, RecordID, QuestionID)
Run Code Online (Sandbox Code Playgroud)

同样:

(Pts, PtsOf)           <->:  (Pts, PtsOf, RecordID, QuestionID)

(Pts, RecordId)        <->:  (Pts, RecordID, QuestionID)

(Pts, QuestionID)      <->:  (Pts, QuestionID, RecordID)

(Pts) INCLUDE (PtsOf)  <->:  (Pts, RecordID, QuestionID) INCLUDE (PtsOf) 

(QuestionID, RecordID) <->:  (QuestionID, RecordID)
Run Code Online (Sandbox Code Playgroud)

对于具有复合主键/唯一键的联合表或类似的表 - 无论是否聚集,经常有需要(a,b)索引的查询和其他会更好地使用(b,a)索引的查询,有时查询需要两者。因此,通常需要这两者。

如果复合聚集键有两个以上的列 - 比如说(a,b,c)- 通常你可能需要一个 on(b,c,a)或 in(d,b)和另一个 on的索引(e,c,a)(这当然分别相当于(b,c,a),(d,b,a,c)(e,c,a,b)。)你不能只从非聚集键的定义,因为列顺序会改变。

不过,该建议有一个好处。可以从INCLUDE部件中删除聚集键列。它们是多余的,只是那里的噪音。


关于题中的索引,非CI on(Pts, PtsOf)等价于(Pts, PtsOf, RecordID, QuestionID),所以和原来的non-CI on 有很大的不同(RecordID, QuestionID) INCLUDE (Pts, PtsOf)。它将比原来使用更多的空间,当然这两个索引对不同类型的查询很有用。

  • (Pts, PtsOf)会是有用的,例如,对于查询WHERE Pts BETWEEN 0 AND 100WHERE Pts = 200 AND PtsOf = 300等等。

  • (RecordID, QuestionID) INCLUDE (Pts, PtsOf)基本表的只有2个聚集键列和2个额外的列(的多)的副本。这是(很少)有用的,它是一种垂直分区的形式。如果您经常有需要表的所有行但只需要这 2 列的查询,那么可能是这些(相当罕见的)情况之一,需要额外的空间和努力来维护此索引是合理的。