SQL索引在数据库优化中占有一个非常大的比例, 一个好的索引的设计,可以让你的效率提高几十甚至几百倍。

什么是索引?

  SQL索引有两种,聚集索引和非聚集索引,索引主要目的是提高了SQL Server系统的性能,加快数据的查询速度与减少系统的响应时间 

下面举两个简单的例子:

图书馆的例子:一个图书馆那么多书,怎么管理呢?建立一个字母开头的目录,例如:a开头的书,在第一排,b开头的在第二排,这样在找什么书就好说了,这个就是一个聚集索引,可是很多人借书找某某作者的,不知道书名怎么办?图书管理员在写一个目录,某某作者的书分别在第几排,第几排,这就是一个非聚集索引

字典的例子:字典前面的目录,可以按照拼音和部首去查询,我们想查询一个字,只需要根据拼音或者部首去查询,就可以快速的定位到这个汉字了,这个就是索引的好处,拼音查询法就是聚集索引,部首查询就是一个非聚集索引.

  看了上面的例子,下面的一句话大家就很容易理解了:聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。就像字段,聚集索引是连续的,a后面肯定是b,非聚集索引就不连续了,就像图书馆的某个作者的书,有可能在第1个货架上和第10个货架上。还有一个小知识点就是:聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。

索引的存储机制

  首先,无索引的表,查询时,是按照顺序存续的方法扫描每个记录来查找符合条件的记录,这样效率十分低下,举个例子,如果我们将字典的汉字随即打乱,没有前面的按照拼音或者部首查询,那么我们想找一个字,按照顺序的方式去一页页的找,这样效率有多底,大家可以想象。

  聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致,其实理解起来非常简单,还是举字典的例子:如果按照拼音查询,那么都是从a-z的,是具有连续性的,a后面就是b,b后面就是c, 聚集索引就是这样的,他是和表的物理排列顺序是一样的,例如有id为聚集索引,那么1后面肯定是2,2后面肯定是3,所以说这样的搜索顺序的就是聚集索引。非聚集索引就和按照部首查询是一样是,可能按照偏房查询的时候,根据偏旁‘弓’字旁,索引出两个汉字,张和弘,但是这两个其实一个在100页,一个在1000页,(这里只是举个例子),他们的索引顺序和数据库表的排列顺序是不一样的,这个样的就是非聚集索引。

  原理明白了,那他们是怎么存储的呢?在这里简单的说一下,聚集索引就是在数据库被开辟一个物理空间存放他的排列的值,例如1-100,所以当插入数据时,他会重新排列整个整个物理空间,而非聚集索引其实可以看作是一个含有聚集索引的表,他只仅包含原表中非聚集索引的列和指向实际物理表的指针。他只记录一个指针,其实就有点和堆栈差不多的感觉了

什么情况下设置索引 

SQL 索引、索引优化_SQL 索引、索引优化 

建立索引的原则

1) 定义主键的数据列一定要建立索引。

2) 定义有外键的数据列一定要建立索引。

3) 对于经常查询的数据列最好建立索引。

4) 对于需要在指定范围内的快速或频繁查询的数据列;

5) 经常用在WHERE子句中的数据列。

6) 经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用。

7) 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。

8) 对于定义为text、p_w_picpath和bit的数据类型的列不要建立索引。

9) 对于经常存取的列避免建立索引 

9) 限制表上的索引数目。对一个存在大量更新操作的表,所建索引的数目一般不要超过3个,最多不要超过5个。索引虽说提高了访问速度,但太多索引会影响数据的更新操作。

10) 对复合索引,按照字段在查询条件中出现的频度建立索引。在复合索引中,记录首先按照第一个字段排序。对于在第一个字段上取值相同的记录,系统再按照第二个字段的取值排序,以此类推。因此只有复合索引的第一个字段出现在查询条件中,该索引才可能被使用,因此将应用频度高的字段,放置在复合索引的前面,会使系统最大可能地使用此索引,发挥索引的作用。

如何创建索引


  创建索引的语法:


CREATE [UNIQUE][CLUSTERED | NONCLUSTERED]  INDEX  index_name  

ON {table_name | view_name} [WITH [index_property [,....n]]

说明:

UNIQUE: 建立唯一索引。

CLUSTERED: 建立聚集索引。

NONCLUSTERED: 建立非聚集索引。

Index_property: 索引属性。

 UNIQUE索引既可以采用聚集索引结构,也可以采用非聚集索引的结构,如果不指明采用的索引结构,则SQL Server系统默认为采用非聚集索引结构。、


  删除索引语法:


DROP INDEX table_name.index_name[,table_name.index_name]

说明:table_name: 索引所在的表名称。

index_name : 要删除的索引名称。


 显示索引信息:


使用系统存储过程:sp_helpindex 查看指定表的索引信息。

执行代码如下:

Exec sp_helpindex book1;



看懂SqlServer查询计划

对于SQL Server的优化来说,优化查询可能是很常见的事情。由于数据库的优化,本身也是一个涉及面比较的广的话题, 因此本文只谈优化查询时如何看懂SQL Server查询计划。毕竟我对SQL Server的认识有限,如有错误,也恳请您在发现后及时批评指正。

首先,打开【SQL Server Management Studio】,输入一个查询语句看看SQL Server是如何显示查询计划的吧。
说明:本文所演示的数据库,是我为一个演示程序专用准备的数据库,

select v.OrderID, v.CustomerID, v.CustomerName, v.OrderDate, v.SumMoney, v.Finished
from
  OrdersView as vwhere v.OrderDate >= '2010-12-1' and v.OrderDate < '2011-12-1';

其中,OrdersView是一个视图,其定义如下:

SELECT     dbo.Orders.OrderID, dbo.Orders.CustomerID, dbo.Orders.OrderDate, 
            dbo.Orders.SumMoney, dbo.Orders.Finished, 
            ISNULL(dbo.Customers.CustomerName, N'') AS CustomerName
FROM         dbo.Orders LEFT OUTER JOIN
                dbo.Customers ON dbo.Orders.CustomerID = dbo.Customers.CustomerID

对于前一句查询,SQL Server给出的查询计划如下(点击工具栏上的【显示估计的执行计划】按钮):

SQL 索引、索引优化_SQL 索引、索引优化_02

从这个图中,我们至少可以得到3个有用的信息:
1. 哪些执行步骤花费的成本比较高。显然,最右边的二个步骤的成本是比较高的。
2. 哪些执行步骤产生的数据量比较多。对于每个步骤所产生的数据量, SQL Server的执行计划是用【线条粗细】来表示的,因此也很容易地从分辨出来。
3. 每一步执行了什么样的动作。

对于一个比较慢的查询来说,我们通常要知道哪些步骤的成本比较高,进而,可以尝试一些改进的方法。 一般来说,如果您不能通过:提高硬件性能或者调整OS,SQL Server的设置之类的方式来解决问题,那么剩下的可选方法通常也只有以下这些了:
1. 为【scan】这类操作增加相应字段的索引。
2. 有时重建索引或许也是有效的,具体情形请参考后文。
3. 调整语句结构,引导SQL Server采用其它的查询方案去执行。
4. 调整表结构(分表或者分区)。

下面再来说说一些很重要的理论知识,这些内容对于执行计划的理解是很有帮助的。

SQL Server 查找记录的方法

说到这里,不得不说SQL Server的索引了。SQL Server有二种索引:聚集索引和非聚集索引。二者的差别在于:【聚集索引】直接决定了记录的存放位置, 或者说:根据聚集索引可以直接获取到记录。【非聚集索引】保存了二个信息:1.相应索引字段的值,2.记录对应聚集索引的位置(如果表没有聚集索引则保存记录指针)。 因此,如果能通过【聚集索引】来查找记录,显然也是最快的。

SQL Server 会有以下方法来查找您需要的数据记录:
1. 【Table Scan】:遍历整个表,查找所有匹配的记录行。这个操作将会一行一行的检查,当然,效率也是最差的。
2. 【Index Scan】:根据索引,从表中过滤出来一部分记录,再查找所有匹配的记录行,显然比第一种方式的查找范围要小,因此比【Table Scan】要快。
3. 【Index Seek】:根据索引,定位(获取)记录的存放位置,然后取得记录,因此,比起前二种方式会更快。
4. 【Clustered Index Scan】:和【Table Scan】一样。注意:不要以为这里有个Index,就认为不一样了。 其实它的意思是说:按聚集索引来逐行扫描每一行记录,因为记录就是按聚集索引来顺序存放的。 而【Table Scan】只是说:要扫描的表没有聚集索引而已,因此这二个操作本质上也是一样的。
5. 【Clustered Index Seek】:直接根据聚集索引获取记录,最快!

所以,当发现某个查询比较慢时,可以首先检查哪些操作的成本比较高,再看看那些操作在查找记录时, 是不是【Table Scan】或者【Clustered Index Scan】,如果确实和这二种操作类型有关,则要考虑增加索引来解决了。 不过,增加索引后,也会影响数据表的修改动作,因为修改数据表时,要更新相应字段的索引。所以索引过多,也会影响性能。 还有一种情况是不适合增加索引的:某个字段用0或1表示的状态。例如可能有绝大多数是1,那么此时加索引根本就没有意义。 这时只能考虑为0或者1这二种情况分开来保存了,分表或者分区都是不错的选择。

如果不能通过增加索引和调整表来解决,那么可以试试调整语句结构,引导SQL Server采用其它的查询方案去执行。 这种方法要求: 1.对语句所要完成的功能很清楚, 2.对要查询的数据表结构很清楚, 3.对相关的业务背景知识很清楚。 如果能通过这种方法去解决,当然也是很好的解决方法了。不过,有时SQL Server比较智能,即使你调整语句结构,也不会影响它的执行计划。

如何比较二个相同功能的SQL语句的性能好坏呢,我建议采用二种方法: 1. 直接把二个查询语句放在【SQL Server Management Studio】,然后去看它们的【执行计划】,SQL Server会以百分比的方式告诉你二个查询的【查询开销】。 这种方法简单,通常也是可以参考的,不过,有时也会不准,具体原因请接着往下看(可能索引统计信息过旧)。
2. 根据真实的程序调用,写相应的测试代码去调用:这种方法就麻烦一些,但是它更能代表现实调用情况, 得到的结果也是更具有参考价值的,因此也是值得的。

更具体执行过程

前面,我给出一张图片,它反映了SQL Server在执行某个查询的执行计划,但它反映的信息可能不太细致,当然,您可以把鼠标指标移动某个节点上,会有以下信息出现:

SQL 索引、索引优化_SQL 索引、索引优化_03


刚好,我装的是中文版的,上面都是汉字,我也不多说了。我要说的是另一种方式的执行过程,比这个包含更多的执行信息, 而且是实际的执行情况。(当然,您也可以继续使用图形方式,在运行查询前点击工具栏上的【包括实际的执行计划】按钮)

让我们再次回到【SQL Server Management Studio】,输入以下语句,然后执行。

set statistics profile on  --返回语句的执行计划,以及语句运行在每一步的实际返回行数统计

SET STATISTICS IO on -- 查看磁盘IO 

set statistics time on -- 查看sql语句分析编译和执行时间  

select v.OrderID, v.CustomerID, v.CustomerName, v.OrderDate, v.SumMoney, v.Finished
from   
OrdersView as vwhere v.OrderDate >= '2010-12-1' and v.OrderDate < '2011-12-1';

打开的一些参数设置:

set statistics profile on  --返回语句的执行计划,以及语句运行在每一步的实际返回行数统计

SET STATISTICS IO on -- 查看磁盘IO 

set statistics time on -- 查看sql语句分析编译和执行时间 

得到的结果如下:

SQL 索引、索引优化_SQL 索引、索引优化_04
SQL 索引、索引优化_SQL 索引、索引优化_05

可以从图片上看到,执行查询后,得到二个表格,上面的表格显示了查询的结果,下面的表格显示了查询的执行过程。相比本文的第一张图片, 这张图片可能在直观上不太友好,但是,它能反映更多的信息,而且尤其在比较复杂的查询时,可能看起来更容易,因为对于复杂的查询,【执行计划】的步骤太多,图形方式会造成图形过大,不容易观察。 而且这张执行过程表格能反映2个很有价值的数据(前二列)。

还是来看看这个【执行过程表格】吧。我来挑几个重要的说一下。
【Rows】:表示在一个执行步骤中,所产生的记录条数。(真实数据,非预期)
【Executes】:表示某个执行步骤被执行的次数。(真实数据,非预期)
【Stmt Text】:表示要执行的步骤的描述。
【EstimateRows】:表示要预期返回多少行数据。

在这个【执行过程表格】中,对于优化查询来说,我认为前三列是比较重要的。对于前二列,我上面也解释了,意思也很清楚。 前二列的数字也大致反映了那些步骤所花的成本,对于比较慢的查询中,应该留意它们。 【Stmt Text】会告诉你每个步骤做了什么事情。对于这种表格,它所要表达的其实是一种树型信息(一行就表示在图形方式下的一个节点), 所以,我建议从最内层开始去读它们。做为示例,我来解释一下这张表格它所表达的执行过程。

第5行:【Clustered Index Seek(OBJECT:([MyNorthwind].[dbo].[Customers].[PK_Customers]), SEEK:([MyNorthwind].[dbo].[Customers].[CustomerID]=[MyNorthwind].[dbo].[Orders].[CustomerID]) ORDERED FORWARD)】, 意思是说,SQL Server在对表Customers做Seek操作,而且是按照【Clustered Index Seek】的方式,对应的索引是【PK_Customers】,seek的值来源于[Orders].[CustomerID]

第4行:【Clustered Index Scan(OBJECT:([MyNorthwind].[dbo].[Orders].[PK_Orders]), WHERE:([MyNorthwind].[dbo].[Orders].[OrderDate]>='2010-12-01 00:00:00.000' AND [MyNorthwind].[dbo].[Orders].[OrderDate]<'2011-12-01 00:00:00.000'))】, 意思是说,SQL Server在对表Customers做Scan操作,即:最差的【表扫描】的方式,原因是,OrderDate列上没有索引,所以只能这样了。

第3行:【Nested Loops(Left Outer Join, OUTER REFERENCES:([MyNorthwind].[dbo].[Orders].[CustomerID]))】, 意思是说,SQL Server把第5行和第4行产生的数据用【Nested Loops】的方式联接起来,其中Outer表是Orders,要联接的匹配操作也在第5行中指出了。

第2行:【Compute Scalar(DEFINE:([Expr1006]=isnull([MyNorthwind].[dbo].[Customers].[CustomerName],N'')))】, 意思是说,要执行一个isnull()函数的调用。具体原因请参考本文前部分中给出视图定义代码。

第1行:【SELECT [v].[OrderID],[v].[CustomerID],[v].[CustomerName],[v].[OrderDate],[v].[SumMoney],[v].[Finished] FROM [OrdersView] [v] WHERE [v].[OrderDate]>=@1 AND [v].[OrderDate]<@2】, 通常第1行就是整个查询,表示它的返回值。


   

转自:

http://www.cnblogs.com/AK2012/archive/2013/01/04/2844283.html

http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html