解析索引中数据列顺序的选择问题

上一篇 / 下一篇  2012-05-08 10:11:23 / 个人分类:数据库

在多个列上面建立索引的时候,我们常常会遇到这样的一个问题“需要把哪个列放在前面”,因为索引中列顺序的不同,会对索引的使用,以至性能产生很大的影响。我们本篇就来分析这个问题。

M8~ q-C x y&r8?0  对于上面的问题,一个常见的回答就是“把选择性最大列放在前面”,这里为了使得后面的讲述顺序进行,我们先来解释一下选择性的含义。选择性是用来描述数据的差异情况的,例如,如果一个表中有1000条数据,其中的某个字段,如ID,如果每一条数据的ID值都不一样,那么ID的选择性就是1;如果其中有 300百个ID是一样的,那么就是说,有700个ID不同,那么选择性就是70%。很显然,数据的选择性越高,那么在上面建立索引效果就越好。

U"|&]#n;f1G0

.u\M"h&` G0u:Y0  下面,我们就来解释一下为什么在多个列上面建立索引的时候需要把选择性高的列放在最前面。51Testing软件测试网/r,ZIJ+Y)o

jf PB7p:Yl B7\r0  也许有朋友听到上面的建议之后,在建立任何基于多个列的索引的时候,都会把表的聚集索引所在的列作为这个多列索引的第一个字段。例如,假设现在表中有4 个字段,ID,Name,Age,BirthDate,其中ID是主键,也是聚集索引,现在我们需要在Name,BirthDate上面建立索引,这个时候,有朋友发现:ID的选择性最高,那么把ID放在新的索引中,势必会更好,于是一个名字为IX_Index的索引就包含了三个列:ID,Name,BirthDate。到后来,可能就发现,如果冒冒然的这样做,使得这个新建的索引没有发挥作用,反而导致性能问题。

-b_J[#{Wi051Testing软件测试网 M]@3fv)j7^

  对于数据库中的每一个索引,都会有相应的统计数据信息,这个统计数据显示了数据的分布情况,统计信息以一个类似柱形的形式表现了数据的分布。数据库只把索引中的第一个列的数据分布情况放在柱形图中,换句话说,这个统计信息显示的就是索引中的第一个数据列的数据分布情况(这里面涉及到的内容有点深,大家可以关注本站点的 “查询优化器内核系列”,里面会讲述到)。

rJs&}#T!e051Testing软件测试网E5U+nI:X!l

  我给大家看个例子吧,假设在SalesOrderDetail表上面有一个索引:X_SalesOrderDetail_ProductID,运行下面的语句:51Testing软件测试网V1pM(U(nf7x jm

RH q-B;f;Y0

  这个索引包含的列有:ProductID,SalesOrderID和SalesOrderDetailID。我们查看它的数据的柱形分布图,如下:51Testing软件测试网zj%}0^i

51Testing软件测试网 GL~o@uh0FE

  我们发现,其中的RANGE_HI_KEY列出的就是ProductID的值,通过图中,我们可以知道:ProductID值为826的数据有305 条,值为831的数据有198条。ProductID的值在826到831之间的数据有110条。查询优化器就是根据这个来估算数据的条数的。

,B |]'ebW4Ij0

  通过上面可以知道:把索引中的哪个列放在前面至关重要,如果把一个选择性很低的列放在前面,那么就导致索引的统计数据显示的数据分布完全改变,可能导致查询优化器选择比较低效的执行计划。51Testing软件测试网+d7|)v |mg

  下面,我们就通过一个例子来进一步的看看这个问题。51Testing软件测试网F"T3Nn1| YEm

  首先,建立一个测试的表,如下:

?vG"i0P)Q)W:A0

6b L e-E6u` |R0

  这个表中有10000条数据,并且这个表是一个堆表,即没有聚集索引的表。并且在这个表中有100个不同的SomeString值,有5000个不同的SomeDate值,而ID是唯一的,全部都不同。51Testing软件测试网Ss2G nw3I

  那么,上面的值的选择性如下:

xS!xXguKT7FE.d!X0

字段名

N QD(@ j2X({{S0

选择性

GF%{l C!M z0

Nwm)q+f%ER0ID51Testing软件测试网edV)R0Z

51Testing软件测试网(Bgv6?Yc-m%N U

100%51Testing软件测试网+jf z:oC

/E#q0y{/K:nKQ(i0SomeString51Testing软件测试网5N!| H"S G/zC/r

51Testing软件测试网T ^u%|^/H u0K

100/10000*100%=1%51Testing软件测试网|Ts7{3vR#N

51Testing软件测试网9{)F xjn

SomeDate

XBy[t B.dH-x0
51Testing软件测试网,BQh/t"E

5000/10000*100%=50%51Testing软件测试网 RK8L5t3~k6F)d

在表中,有一个非聚集索引,假设名字为Idx_test,包含了表中的三个值,三个列在索引中的顺序为:ID,SomeDate,SomeString,按照选择性排序,确实不错!

[-R*l'O\-n051Testing软件测试网i$O UL8[Ds

  1. …  WHERE ID = @ID AND SomeDate = @dt AND SomeString = @str  
  2. …  WHERE ID = @ID AND SomeDate = @dt  
  3. …  WHERE ID = @ID
51Testing软件测试网x#Z4P0X0SE1KX6{

  换句话说,就是这个索引只在查询中的Where/Join的列按照索引中的列的顺序使用的时候才有效。如果查询是这样的,如下:51Testing软件测试网-R%y.J;{*Z T6{}

51Testing软件测试网n` F5lq!M2e

  对于上面的索引,只有在类似下面的查询结构中发挥作用,如下:

(L:@ c X{ q051Testing软件测试网B8K ct"@'nRb

…  WHERE SomeDate = @dt或者…  SomeDate = @dt AND SomeString = @str
51Testing软件测试网'Fi4_ j ZE0Z!dK

RWtJ4K%| Xk0  那么,这个索引就不会上面的查询中使用了,那么查询在执行的时候就会扫描整表了。51Testing软件测试网!m(e~"m?n3t`

Z)|]4k*j})Z#j)j0  我们通过执行计划来看看是不是这样的。51Testing软件测试网0Cm(mu-s]`2z;BV0f

4[7v*\ @x*Gj0  对于,WHERE ID = @ID的查询,执行计划如下:

D-^K+TAP i v Mnl0

51Testing软件测试网o8{v.W%l-~s@

  很显然,执行了Seek操作,是很快的。51Testing软件测试网W8r#zs"mq2rn-K ~

  对于WHERE ID = @ID AND SomeDate = @dt的查询,执行计划如下:51Testing软件测试网xU I+}/J#]4e

51Testing软件测试网ALU)] ~D y

  还是进行了Seek操作。51Testing软件测试网/v+gZ-n&s3gnz

  那么对于… SomeDate = @dt AND SomeString = @str的查询,如下:

_;B d1u9I J0

51Testing软件测试网Y'z p2k/MH5b3c

  大家可以看到,这个时候已经开始进行全表扫描了。

{1TWfv0

  我们本篇讲述了在索引的进行列的相等操作时候,列的顺序问题,我们下一篇就讲述如果是在列上进行不等操作,例如ID>1,那么索引中的列的顺序还是这样进行吗?

-jX8?)PI*gsPh0

TAG:

 

评分:0

我来说两句

Open Toolbar