索引中列的顺序有多重要?

我听说你应该在索引声明的开始部分放置最有select性的列。 例:

CREATE NONCLUSTERED INDEX MyINDX on Table1 ( MostSelective, SecondMost, Least ) 

首先,我说的是正确的? 如果是这样,我是否可以通过重新排列索引中列的顺序来看到性能上的巨大差异,还是更像是“做得好”的做法?

我问的原因是因为在通过DTA进行查询之后,build议我创build一个索引,它与现有索引中的几乎所有列都相同,只是顺序不同。 我正在考虑只是将缺less的列添加到现有的索引,并称它为好。 思考?

看看这样的索引:

 Cols 1 2 3 ------------- | | 1 | | | A |---| | | | 2 | | |---|---| | | | | | | | 1 | 9 | | B | | | | |---| | | | 2 | | | |---| | | | 3 | | |---|---| | 

首先看看A是如何限制的,因为你的第一列消除了比第二列第一列限制更多的结果? 如果你想知道指标必须穿越第一栏,第二栏等等,你会发现,在第一遍中,大部分的结果都是一样的,这使得第二步更快。

另一种情况是,如果您在第3列中查询,优化器甚至不会使用索引,因为在缩小结果集方面没有任何帮助。 任何时候你在查询中,在下一步之前缩小处理结果的数量意味着更好的性能。

由于索引也是以这种方式存储的,所以当您查询索引时,索引中没有回溯到第一列。

简而言之:不,不适合演出,有真正的演出效益。

列的顺序是至关重要的。 现在哪个顺序是正确的,这取决于你将如何查询它。 一个索引可以用来做一个精确的search或范围扫描。 精确查找是指定索引中所有列的值,并且查询完全位于该行所关注的位置。对于查询,列的顺序是不相关的。 范围扫描是仅指定某些列的情况,在此情况下,订单变得重要。 只有在指定了最左边的列时,SQL Server才能使用范围扫描的索引,然后只有在指定了下一个最左边的列时,才能使用该索引。 如果在(A,B,C)上有一个索引,它可以用于范围扫描A=@a ,对于A=@a AND B=@b不是 B=@b ,对于C=@c B=@b AND C=@c 。 在A=@a AND C=@c情况下混合一个,因为在A=@a一部分将使用索引,但C=@c不是(查询将扫描所有B值为A=@a ,将不要“跳过”到C=@c )。 其他数据库系统具有所谓的“跳过扫描”操作符,当未指定外部列时,可以利用索引中的内部列的某些优点。

掌握这些知识后,您可以再次查看索引定义。 只有在指定了MostSelective列时(MostSelective, SecondMost, Least)上的索引才会生效。 但这是最有select性的,内部列的相关性将迅速降低。 很多时候你会发现一个更好的索引(MostSelective) include (SecondMost, Least)(MostSelective, SecondMost) include (Least) 。 由于内部列的相关性较低,因此将低select性列放置在索引中的这些正确位置上使得它们只是search的噪声,所以将它们移出中间页面并将其保留在叶页上是有意义的,因为查询coverability的目的。 换句话说,把它们移到INCLUDE。 随着Least列的大小增加,这变得更重要。 这个想法是,这个索引只能有利于将MostSelective指定为精确值或范围的查询,并且该列是最有select性的,它已经在很大程度上限制了候选行。

另一方面, (Least, SecondMost, MostSelective)的索引可能看起来是一个错误,但它确实是一个强大的索引。 因为它具有Least外层查询,所以它可以用于需要在低select性列上聚合结果的查询。 这样的查询在OLAP和分析数据仓库中很普遍,而这正是这些索引有很好的案例。 这些索引实际上是优秀的聚集索引,正是因为它们组织了大块相关行的物理布局(相同的最小值,通常表示某种类别或types),并且便于分析查询。

所以,不幸的是,没有“正确的”顺序。 你不应该遵循任何cookie的配方,而是分析你将要使用的查询模式,并决定哪个索引列的顺序是正确的。

您应该在索引声明的开始处放置最具select性的列。

正确。 索引可以是由多个列组成的组合 – 由于最左边的原则,顺序很重要。 原因是,数据库从左到右检查列表,并且必须find与定义的顺序匹配的相应的列引用。 例如,在具有列的地址表上有一个索引:

  • 地址

任何使用address列的查询都可以使用索引,但如果查询只有city和/或state引用 – 索引不能使用。 这是因为最左边的列没有被引用。 查询性能应该告诉你哪个是最优的 – 单个索引,或者多个具有不同顺序的组合。 好读:金伯利Tripp 的引爆点

Remus说这取决于你的工作量。

我想解决接受答案的误导性方面。

对于在索引中的所有列上执行等式search的查询,没有显着差异。

以下创build两个表并使用相同的数据填充它们。 唯一的区别是,有一个按键从大多数到最不具有select性,而另一个则相反。

 CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least CHAR(1), Filler CHAR(4000) null); CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least CHAR(1), Filler CHAR(4000) null); CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least); CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective); INSERT INTO Table1 (MostSelective, SecondMost, Least) output inserted.* into Table2 SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~' FROM master..spt_values WHERE type = 'P' AND number >= 0 ORDER BY number; 

现在对这两个表进行查询…

 SELECT * FROM Table1 WHERE MostSelective = REPLICATE('P', 800) AND SecondMost = 3 AND Least = '~'; SELECT * FROM Table2 WHERE MostSelective = REPLICATE('P', 800) AND SecondMost = 3 AND Least = '~'; 

…他们两人都使用一个好的指数,两个都给了完全相同的成本。

在这里输入图像说明

被接受的答案中的ASCII艺术实际上并不是如何构build索引的。 Table1的索引页面如下所示(单击图像以全尺寸打开)。

在这里输入图像说明

索引页面包含包含整个键的行(在这种情况下,实际上为行标识符附加了一个额外的键列,因为索引没有被声明为唯一的,但是可以忽略关于这个的更多信息 )。

对于上面的查询,SQL Server不关心列的select性。 它执行根页面的二进制search,发现密钥 (PPP...,3,~ )>=(JJJ...,1,~ )< (SSS...,3,~ )它应该阅读第1:1181:118 。 然后对该页面上的关键条目执行二分查找,并find要往下走的叶子页面。

按照select性顺序改变索引不会影响二进制search的预期关键比较次数,也不会影响需要导航以进行索引search的页数。 充其量,它可能稍微加快关键比较本身。

有时,首先sorting最有select性的索引对于工作负载中的其他查询是有意义的。

例如,如果工作负载包含以下两种forms的查询。

 SELECT * ... WHERE MostSelective = 'P' SELECT * ...WHERE Least = '~' 

以上指标并不涵盖其中任何一个。 MostSelective是足够有select性的做一个寻求和查找值得的计划,但对Least的查询不是。

然而,这种情况(在复合索引的主要列的子集上没有覆盖索引查找)只是索引可以帮助的一个可能的查询类别。 如果你从来没有真正用MostSelective自己或者MostSelective, SecondMost的组合searchMostSelective, SecondMost并且总是通过所有三列的组合search,那么这个理论上的优势对你来说是无用的。

相反的查询如

 SELECT MostSelective, SecondMost, Least FROM Table2 WHERE Least = '~' ORDER BY SecondMost, MostSelective 

将通过具有相同顺序的通用规定的顺序来帮助,因为它涵盖了查询,可以支持查找并以期望的顺序返回行来引导。

所以这是一个经常重复的build议,但最多是对其他查询的潜在好处的启发式 – 而且它不能替代实际查看您的工作量。