索引机制、索引技术、索引设计准则
1.横向比较数据库中不同的索引机制
一、引言
对数据库索引的关注从未淡出我的们的讨论,那么数据库索引是什么样的?聚集索引与非聚集索引有什么不同?希望本文对各位同仁有一定的帮助。有不少存疑的地方,诚心希望各位不吝赐教指正,共同进步。
二、B-Tree
我们常见的数据库系统,其索引使用的数据结构多是B-Tree或者B+Tree。例如,MsSql使用的是B+Tree,Oracle及Sysbase使用的是B-Tree。所以在最开始,简单地介绍一下B-Tree。
B-Tree不同于Binary Tree(二叉树,最多有两个子树),一棵M阶的B-Tree满足以下条件:
1)每个结点至多有M个孩子;
2)除根结点和叶结点外,其它每个结点至少有M/2个孩子;
3)根结点至少有两个孩子(除非该树仅包含一个结点);
4)所有叶结点在同一层,叶结点不包含任何关键字信息;
5)有K个关键字的非叶结点恰好包含K+1个孩子;
另外,对于一个结点,其内部的关键字是从小到大排序的。以下是B-Tree(M=4)的样例:
对于每个结点,主要包含一个关键字数组Key[],一个指针数组(指向儿子)Son[]。在B-Tree内,查找的流程是:使用顺序查找(数组长度较短时)或折半查找方法查找Key[]数组,若找到关键字K,则返回该结点的地址及K在Key[]中的位置;否则,可确定K在某个Key[i]和Key[i+1]之间,则从Son[i]所指的子结点继续查找,直到在某结点中查找成功;或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。
接着,我们使用以下图片演示如何生成B-Tree(M=4,依次插入1~6):
从图可见,当我们插入关键字4时,由于原结点已经满了,故进行分裂,基本按一半的原则进行分裂,然后取出中间的关键字2,升级(这里是成为根结点)。其它的依类推,就是这样一个大概的过程。
三、数据库索引
1.什么是索引
在数据库中,索引的含义与日常意义上的“索引”一词并无多大区别(想想小时候查字典),它是用于提高数据库表数据访问速度的数据库对象。
A)索引可以避免全表扫描。多数查询可以仅扫描少量索引页及数据页,而不是遍历所有数据页。
B)对于非聚集索引,有些查询甚至可以不访问数据页。
C)聚集索引可以避免数据插入操作集中于表的最后一个数据页。
D)一些情况下,索引还可用于避免排序操作。
当然,众所周知,虽然索引可以提高查询速度,但是它们也会导致数据库系统更新数据的性能下降,因为大部分数据更新需要同时更新索引。
2.索引的存储
一条索引记录中包含的基本信息包括:键值(即你定义索引时指定的所有字段的值)+逻辑指针(指向数据页或者另一索引页)。
当你为一张空表创建索引时,数据库系统将为你分配一个索引页,该索引页在你插入数据前一直是空的。此页此时既是根结点,也是叶结点。每当你往表中插入一行数据,数据库系统即向此根结点中插入一行索引记录。当根结点满时,数据库系统大抵按以下步骤进行分裂:
A)创建两个儿子结点
B)将原根结点中的数据近似地拆成两半,分别写入新的两个儿子结点
C)根结点中加上指向两个儿子结点的指针
通常状况下,由于索引记录仅包含索引字段值(以及4-9字节的指针),索引实体比真实的数据行要小许多,索引页相较数据页来说要密集许多。一个索引页可以存储数量更多的索引记录,这意味着在索引中查找时在I/O上占很大的优势,理解这一点有助于从本质上了解使用索引的优势。
3.索引的类型
A)聚集索引,表数据按照索引的顺序来存储的。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。
B)非聚集索引,表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针,该层紧邻数据页,其行数量与数据表行数据量一致。
在一张表上只能创建一个聚集索引,因为真实数据的物理顺序只可能是一种。如果一张表没有聚集索引,那么它被称为“堆集”(Heap)。这样的表中的数据行没有特定的顺序,所有的新行将被添加的表的末尾位置。
4.聚集索引
在聚集索引中,叶结点也即数据结点,所有数据行的存储顺序与索引的存储顺序一致。
1)聚集索引与查询操作
如上图,我们在名字字段上建立聚集索引,当需要在根据此字段查找特定的记录时,数据库系统会根据特定的系统表查找的此索引的根,然后根据指针查找下一个,直到找到。例如我们要查询“Green”,由于它介于[Bennet,Karsen],据此我们找到了索引页1007,在该页中“Green”介于[Greane, Hunter]间,据此我们找到叶结点1133(也即数据结点),并最终在此页中找以了目标数据行。
此次查询的IO包括3个索引页的查询(其中最后一次实际上是在数据页中查询)。这里的查找可能是从磁盘读取(Physical Read)或是从缓存中读取(Logical Read),如果此表访问频率较高,那么索引树中较高层的索引很可能在缓存中被找到。所以真正的IO可能小于上面的情况。
2)聚集索引与插入操作
最简单的情况下,插入操作根据索引找到对应的数据页,然后通过挪动已有的记录为新数据腾出空间,最后插入数据。
如果数据页已满,则需要拆分数据页(页拆分是一种耗费资源的操作,一般数据库系统中会有相应的机制要尽量减少页拆分的次数,通常是通过为每页预留空间来实现):
A)在该使用的数据段(extent)上分配新的数据页,如果数据段已满,则需要分配新段。
B)调整索引指针,这需要将相应的索引页读入内存并加锁。
C)大约有一半的数据行被归入新的数据页中。
D)如果表还有非聚集索引,则需要更新这些索引指向新的数据页。
特殊情况:
A)如果新插入的一条记录包含很大的数据,可能会分配两个新数据页,其中之一用来存储新记录,另一存储从原页中拆分出来的数据。
B)通常数据库系统中会将重复的数据记录存储于相同的页中。
C)类似于自增列为聚集索引的,数据库系统可能并不拆分数据页,页只是简单的新添数据页。
3)聚集索引与删除操作
删除行将导致其下方的数据行向上移动以填充删除记录造成的空白。
如果删除的行是该数据页中的最后一行,那么该数据页将被回收,相应的索引页中的记录将被删除。如果回收的数据页位于跟该表的其它数据页相同的段上,那么它可能在随后的时间内被利用。如果该数据页是该段的唯一一个数据页,则该段也被回收。
对于数据的删除操作,可能导致索引页中仅有一条记录,这时,该记录可能会被移至邻近的索引页中,原索引页将被回收,即所谓的“索引合并”。
5.非聚集索引
非聚集索引与聚集索引相比:
A)叶子结点并非数据结点
B)叶子结点为每一真正的数据行存储一个“键-指针”对
C)叶子结点中还存储了一个指针偏移量,根据页指针及指针偏移量可以定位到具体的数据行。
D)类似的,在除叶结点外的其它索引结点,存储的也是类似的内容,只不过它是指向下一级的索引页的。
聚集索引是一种稀疏索引,数据页上一级的索引页存储的是页指针,而不是行指针。而对于非聚集索引,则是密集索引,在数据页的上一级索引页它为每一个数据行存储一条索引记录。
对于根与中间级的索引记录,它的结构包括:
A)索引字段值
B)RowId(即对应数据页的页指针+指针偏移量)。在高层的索引页中包含RowId是为了当索引允许重复值时,当更改数据时精确定位数据行。
C)下一级索引页的指针
对于叶子层的索引对象,它的结构包括:
A)索引字段值
B)RowId
1)非聚集索引与查询操作
针对上图,如果我们同样查找“Green”,那么一次查询操作将包含以下IO:3个索引页的读取+1个数据页的读取。同样,由于缓存的关系,真实的IO实际可能要小于上面列出的。
2)非聚集索引与插入操作
如果一张表包含一个非聚集索引但没有聚集索引,则新的数据将被插入到最末一个数据页中,然后非聚集索引将被更新。如果也包含聚集索引,该聚集索引将被用于查找新行将要处于什么位置,随后,聚集索引、以及非聚集索引将被更新。
3)非聚集索引与删除操作
如果在删除命令的Where子句中包含的列上,建有非聚集索引,那么该非聚集索引将被用于查找数据行的位置,数据删除之后,位于索引叶子上的对应记录也将被删除。如果该表上有其它非聚集索引,则它们叶子结点上的相应数据也要删除。
如果删除的数据是该数所页中的唯一一条,则该页也被回收,同时需要更新各个索引树上的指针。
由于没有自动的合并功能,如果应用程序中有频繁的随机删除操作,最后可能导致表包含多个数据页,但每个页中只有少量数据。
6.索引覆盖
索引覆盖是这样一种索引策略:当某一查询中包含的所需字段皆包含于一个索引中,此时索引将大大提高查询性能。
包含多个字段的索引,称为复合索引。索引最多可以包含31个字段,索引记录最大长度为600B。如果你在若干个字段上创建了一个复合的非聚集索引,且你的查询中所需Select字段及Where,Order By,Group By,Having子句中所涉及的字段都包含在索引中,则只搜索索引页即可满足查询,而不需要访问数据页。由于非聚集索引的叶结点包含所有数据行中的索引列值,使用这些结点即可返回真正的数据,这种情况称之为“索引覆盖”。
在索引覆盖的情况下,包含两种索引扫描:
A)匹配索引扫描
B)非匹配索引扫描
1)匹配索引扫描
此类索引扫描可以让我们省去访问数据页的步骤,当查询仅返回一行数据时,性能提高是有限的,但在范围查询的情况下,性能提高将随结果集数量的增长而增长。
针对此类扫描,索引必须包含查询中涉及的的所有字段,另外,还需要满足:Where子句中包含索引中的“引导列”(Leading Column),例如一个复合索引包含A,B,C,D四列,则A为“引导列”。如果Where子句中所包含列是BCD或者BD等情况,则只能使用非匹配索引扫描。
2)非配置索引扫描
正如上述,如果Where子句中不包含索引的导引列,那么将使用非配置索引扫描。这最终导致扫描索引树上的所有叶子结点,当然,它的性能通常仍强于扫描所有的数据页。
2.关系数据库的索引技术
摘 要:索引是影响关系数据库(RDBMS)性能的重要因素之一。当今常用的关系数据库,如SQL Server、Sybase、Oracle、DB2等,为了提高性能,都提供相应的索引机制。本文以微软的SQL Server数据库为例,介绍索引的概念、使用方法以及日常维护。
关键词:数据库、索引、检索
1 索引的概念
索引是一个单独的、物理的数据库结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。表的存储由两部分组成,一部分用来存放数据页面,另一部分存放索引页面。通常,索引页面相对于数据页面来说小得多。数据检索花费的大部分开销是磁盘读写,没有索引就需要从磁盘上读表的每一个数据页,如果有索引,则只需查找索引页面就可以了。所以建立合理的索引,就能加速数据的检索过程。
SQL Server采用B-树结构的索引,根据索引的顺序与数据表的物理顺序是否相同可以分为:聚簇索引(clustered index)和非聚簇索引(nonclustered index)。
(1)聚簇索引重新组织表中的数据以按指定的一个或多个列的值排序。聚簇索引的叶节点包含实际的数据,因此用它查找数据很快,但每个表只能建一个聚簇索引。
(2)非聚簇索引不重新组织表中的数据,它的叶节点中存储了组成非聚簇索引的列的值和行定位指针。一个表可以建249 个非聚簇索引。
通俗的说,汉语字典的正文就是一个建立在拼音基础上的聚簇索引,以英文字母“a”开头并以“z”结尾。比如,我们要查“阿”字,就会翻开字典的第一页,因为 “阿”的拼音是“a”,所以排在字典的前面。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明字典中没有这个字。同样的,如果查“做” 字,就会把字典翻到最后。
字典的“偏旁部首”是非聚簇索引。比如我们要查“阿”字,在查部首之后,看到部首检字表中“阿”的页码是1页,“阿”的上面是“际”字,但页码却是277 页,“阿”的下面是“陇”字,页码是416页。很显然,这些字并不是真正的分别位于“阿”字的上下方,现在看到的连续的“际、阿、陇”三字实际上就是他们在非聚簇索引中的排序,是字典正文中的字在非聚簇索引中的映射。
2 索引的使用
1)聚簇索引的使用
在聚簇索引下,数据在物理上按顺序排在数据页上,重复值也排在一起,因而在那些包含范围检查(between、<、<=、>、& gt;=)或使用group by、order by的查询时,一旦找到具有范围中第一个键值的行,具有后续索引值的行必然连在一起,不必进一步搜索,避免了大范围扫描,可以大大提高查询速度。
聚簇索引的侯选列是:
u 经常按范围存取的列,如date>”20050101” and date< “20050131”;
u 经常在where子句中使用并且插入是随机的主键列;
u 在group by或order by中使用的列;
u 在连接操作中使用的列。
2)非聚簇索引的使用
由于非聚簇索引的叶级点不包含实际的数据,因此它检索效率较低,但一个表只能建一个聚簇索引,当用户需要建立多个索引时就需要使用非聚簇索引了。在建立非聚簇索引时,要权衡索引对查询速度的加快与降低修改速度之间的利弊。
在下面情况中使用非聚簇索引:
u 常用于集合函数(如Sum,....)的列;
u 常用于join, order by, group by的列;
u 查寻出的数据不超过表中数据量的20%。
表1 索引使用情况分析表
情况描述 | 使用聚簇索引 | 使用非聚簇索引 |
用于返回某范围内数据的列 | 应 | 不应 |
经常被用作分组排序的列 | 应 | 应 |
小数目不同值的列 | 应 | 不应 |
连接操作使用的列 | 应 | 应 |
频繁更新、修改的列 | 不应 | 应 |
一个或极少不同值的列 | 不应 | 不应 |
大数目不同值的列 | 不应 | 应 |
3)创建索引需要注意的要点
1) 慎重选择作为聚簇索引的列
默认情况下,SQL Server用主键创建聚簇索引。这种做法常常造成聚簇索引的浪费。通常,我们会为每个表建立一个ID列,以区分每条数据,并且该列是自动增大的,步长一般为1。如果我们把这个列设为主键,SQL Server会将此列默认为聚簇索引。这样做可以使数据在数据库中按ID进行物理排序,但这种做法在实际应用中意义并不大。根据前面谈到的聚簇索引的定义和使用情况可以看出,使用聚簇索引的最大好处就是能够根据查询要求,迅速返回某个范围内的数据,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们不太可能用ID号来进行查询。这就使聚簇索引成为摆设,造成资源浪费。其次,让每个值都不同的ID列作为聚簇索引也不符合“大数目的不同值情况下不应建立聚簇索引”规则。
一般情况下,数据库应用系统进行数据检索都离不开“ 用户名(代码)”、“日期”字段。以笔者所用的HIS系统(医院管理信息系统)为例,我们进行费用、处方、检查单等信息检索时需要根据“住院号”和“日期 ”这两个字段来返回特定范围内的数据。下面我们分几种情况观察在不同索引条件下查询相同内容所用的时间。
假设病人费用表名为“brfy”,其中住院号字段名为“zyh”,日期字段名为“riqi”,要求是从表brfy中检索zyh为“028246”的病人2005年3月1日到20日的费用,对应的SQL语句如下:
Select * from brfy where zyh=’028246’ and riqi>=’20050301’ and riqi<=’20050320’;
第一种情况,用ID列建立聚簇索引,不为zyh和riqi建立索引,查询时间为87秒。
第二种情况,用ID列建立聚簇索引,为zyh和riqi两列建立非聚簇索引(zyh在前),查询时间为33秒。
第三种情况,用zyh和riqi两列建立聚簇索引(zyh在前),查询时间为2秒。
由以上分析可以看出聚簇索引是非常宝贵的,应该为经常用于检索某个范围内数据的列或group by、order by等子句的列建立聚簇索引,这样能够极大的提高系统性能。
2) 重视以多个列创建的索引中列的顺序问题
一些用户认为只要合理的选择列建立索引,不必关心列的顺序就可以提高检索速度,这种观点是错误的。多列索引中列的先后顺序应该和实际应用中where、 group by或order by等子句里列的放置位置相同。参考上面举的例子,在第二、第三种情况下,如果把riqi放在zyh前面,执行上述SQL语句就不会用到这两个索引,检索的时间也会变得很长。
3 索引的维护
数据库系统运行一段时间后,随着数据行的插入、删除和数据页的分裂,索引对系统的优化性能就会大大降低。这时候,我们需要对索引进行分析和重建。
SQL Server使用DBCC SHOWCONTIG确定是否需要重建表的索引。在 SQL Server的查询分析器中输入命令:
Use database_name
Declare @table_id int
Set @table_id=object_id ('Employee')
Dbcc showcontig (@table_id)
在命令返回的参数中Scan Density 是索引性能的关键指示器,这个值越接近100%越好,一般在低于90%的情况下,就需要重建索引。重建索引可以使用DBCC DBREINDEX,使用方式如下:
dbcc dbreindex('表名', 索引名, 填充因子) /*填充因子一般为90或100*/
如果重建后,Scan Density还没有达到100%,可以重建该表的所有索引:
dbcc dbreindex('表名', '', 填充因子)
在良好的数据库设计基础上,有效地使用索引是数据库应用系统取得高性能的基础。然而,任何事物都具有两面性,索引也不例外。索引的建立需要占用额外的存储空间,并且在增、删、改操作中也会增加一定的工作量,因此,在适当的地方增加适当的索引并从不合理的地方删除次要的索引,将有助于优化那些性能较差的数据库应用系统。实践表明,合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来,才能产生最佳的优化方案
3.浅谈数据库索引
数据库索引是为了增加查询速度而对表字段附加的一种标识。见过很多人机械的理解索引的概念,认为增加索引只有好处没有坏处。这里想把之前的索引学习笔记总结一下:
首先明白为什么索引会增加速度,DB在执行一条Sql语句的时候,默认的方式是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合。如果我们对某一字段增加索引,查询时就会先去索引列表中一次定位到特定值的行数,大大减少遍历匹配的行数,所以能明显增加查询的速度。那么在任何时候都应该加索引么?这里有几个反例:1、如果每次都需要取到所有表记录,无论如何都必须进行全表扫描了,那么是否加索引也没有意义了。2、对非唯一的字段,例如“性别”这种大量重复值的字段,增加索引也没有什么意义。3、对于记录比较少的表,增加索引不会带来速度的优化反而浪费了存储空间,因为索引是需要存储空间的,而且有个致命缺点是对于update/insert/delete的每次执行,字段的索引都必须重新计算更新。
那么在什么时候适合加上索引呢?我们看一个Mysql手册中举的例子,这里有一条sql语句:
SELECT c.companyID, c.companyName FROM Companies c, User u WHERE c.companyID = u.fk_companyID AND c.numEmployees >= 0 AND c.companyName LIKE '%i%' AND u.groupID IN (SELECT g.groupID FROM Groups g WHERE g.groupLabel = 'Executive')
这条语句涉及3个表的联接,并且包括了许多搜索条件比如大小比较,Like匹配等。在没有索引的情况下Mysql需要执行的扫描行数是77721876行。而我们通过在companyID和groupLabel两个字段上加上索引之后,扫描的行数只需要134行。在Mysql中可以通过Explain Select来查看扫描次数。可以看出来在这种联表和复杂搜索条件的情况下,索引带来的性能提升远比它所占据的磁盘空间要重要得多。
那么索引是如何实现的呢?大多数DB厂商实现索引都是基于一种数据结构——B树。因为B树的特点就是适合在磁盘等直接存储设备上组织动态查找表。B树的定义是这样的:一棵m(m>=3)阶的B树是满足下列条件的m叉树:
1、每个结点包括如下作用域(j, p0, k1, p1, k2, p2, ... ki, pi) 其中j是关键字个数,p是孩子指针
2、所有叶子结点在同一层上,层数等于树高h
3、每个非根结点包含的关键字个数满足[m/2-1]<=j<=m-1
4、若树非空,则根至少有1个关键字,若根非叶子,则至少有2棵子树,至多有m棵子树
看一个B树的例子,针对26个英文字母的B树可以这样构造:
可以看到在这棵B树搜索英文字母复杂度只为o(m),在数据量比较大的情况下,这样的结构可以大大增加查询速度。然而有另外一种数据结构查询的虚度比B树更快——散列表。Hash表的定义是这样的:设所有可能出现的关键字集合为u,实际发生存储的关键字记为k,而|k|比|u|小很多。散列方法是通过散列函数h将u映射到表T[0,m-1]的下标上,这样u中的关键字为变量,以h为函数运算结果即为相应结点的存储地址。从而达到可以在o(1)的时间内完成查找。
然而散列表有一个缺陷,那就是散列冲突,即两个关键字通过散列函数计算出了相同的结果。设m和n分别表示散列表的长度和填满的结点数,n/m为散列表的填装因子,因子越大,表示散列冲突的机会越大。
因为有这样的缺陷,所以数据库不会使用散列表来做为索引的默认实现,Mysql宣称会根据执行查询格式尝试将基于磁盘的B树索引转变为和合适的散列索引以追求进一步提高搜索速度。我想其它数据库厂商也会有类似的策略,毕竟在数据库战场上,搜索速度和管理安全一样是非常重要的竞争点
4.数据库学习-索引设计准则
设计索引时需要遵循的准则包括:数据库准则、查询准则、列准则。以下详细列出了各个准则详细说明。其中1-3属于数据库准则;2-8属于查询准则;9-15属于列准则。
1. 一个表如果建有大量索引会影响INSERT、UPDATE和DELETE语句的性能,因为在表中的数据更改时,所有索引都须进行适当的调整。 避免对经常更新的表进行过多的索引,并且索引应保持较窄,就是说,列要尽可能少。
2. 使用多个索引可以提高更新少而数据量大的查询的性能。大量索引可以提高不修改数据的查询(例如SELECT语句)的性能,因为查询优化器有更多的索引可供选择,从而可以确定最快的访问方法。
3. 对小表进行索引可能不会产生优化效果,因为查询优化器在遍历用于搜索数据的索引时,花费的时间可能比执行简单的表扫描还长。因此,小表的索引可能从来不用,但仍必须在表中的数据更改时进行维护。
4. 视图包含聚合、表联接或聚合和联接的组合时,视图的索引可以显著地提升性能。若要使查询优化器使用视图,并不一定非要在查询中显式引用该视图。
5. 为经常用于查询中的谓词和联接条件的所有列创建非聚集索引。避免添加不必要的列。添加太多索引列可能对磁盘空间和索引维护性能产生负面影响。
6. 涵盖索引可以提高查询性能,因为符合查询要求的全部数据都存在于索引本身中。也就是说,只需要索引页,而不需要表的数据页或聚集索引来检索所需数据,因此,减少了总体磁盘I/O。例如,对某一表(其中对列a、列b和列c创建了组合索引)的列a和列b的查询,仅仅从该索引本身就可以检索指定数据。
7. 将批量insert、update的操作写入单个语句内,而不要使用多个SQL语句操作相同的行。仅使用一个语句,就可以利用优化的索引维护。
8. 评估查询类型以及如何在查询中使用列。例如,在完全匹配查询类型中使用的列就适合用于非聚集索引或聚集索引。
9. 对于聚集索引,请保持较短的索引键长度。另外,对唯一列或非空列创建聚集索引可以使聚集索引获益。
10. 不能将ntext、text、image、varchar(max)、nvarchar(max) 和varbinary(max) 数据类型的列指定为索引键列。不过,varchar(max)、nvarchar(max)、varbinary(max) 和xml数据类型的列可以作为非键索引列参与非聚集索引。
11. xml数据类型的列只能在XML索引中用作键列。
12. 检查列的唯一性。在同一个列组合的唯一索引而不是非唯一索引提供了有关使索引更有用的查询优化器的附加信息。
13. 在列中检查数据分布。通常情况下,为包含很少唯一值的列创建索引或在这样的列上执行联接将导致长时间运行的查询。这是数据和查询的基本问题,通常不识别这种情况就无法解决这类问题。例如,如果物理电话簿按姓的字母顺序排序,而城市里所有人的姓都是Smith或Jones,则无法快速找到某个人。
14. 如果索引包含多个列,则应考虑列的顺序。用于等于 (=)、大于 (>)、小于 (<) 或BETWEEN搜索条件的WHERE子句或者参与联接的列应该放在最前面。其他列应该基于其非重复级别进行排序,就是说,从最不重复的列到最重复的列。
例如,如果将索引定义为LastName、FirstName,则该索引在搜索条件为WHERE LastName = ‘Smith’ 或WHERE LastName = Smith AND FirstName LIKE ‘J%’ 时将很有用。不过,查询优化器不会将此索引用于基于FirstName (WHERE FirstName = ‘Jane’) 而搜索的查询。
15. 考虑对计算列进行索引。