0.參考文獻
http://msdn.microsoft.com/zh-cn/library/ms172984(SQL.90).aspx
1.實驗數據
我們將利用AdvantureWords2008R2中的Sales.SalesOrderDetail表,其中有12萬條數據,非常適合用於測試。不過我們不直接在這張表上做測試,因為這張表上已經有索引了。我們需要新建一張表,將該表中的數據導入我們新建的test和test2表。test和test2的創建方法有兩種,我們選擇第二種。

--實驗1:使用INSERT INTO tablename(col1,col2...) SELECT 往已存在表復制數據 --1.創建表 create table OrderDetail2 ( SalesOrderDetailID int primary key not null, SalesOrderID int, CarrierTrackingNumber nvarchar(25) ); --2.插入數據 Insert into OrderDetail2(SalesOrderDetailID,SalesOrderID,CarrierTrackingNumber) select SalesOrderDetailID,SalesOrderID,CarrierTrackingNumber from AdventureWorks2008R2.Sales.SalesOrderDetail --實驗1結束---------------------------------------------- --實驗2:使用SELECT INTO創建表並復制數據 select * into test from AdventureWorks2008R2.Sales.SalesOrderDetail select * into test2 from AdventureWorks2008R2.Sales.SalesOrderDetail --實驗2結束----------------------------------------------
2.聚集索引與非聚集索引對查詢效率的影響
下面我們將通過實驗來說明聚集索引和非聚集索引在查詢效率上的影響。根據logic read以及execution plan我們能夠更加清晰知道索引的結構,以及sql server是如何查找數據的。

--實驗3:聚集索引跟非聚集索引對查詢效率的影響。----------------------- --1.統計io跟time set statistics io on set statistics time on set statistics profile on --2.在test表沒有索引,因此是堆,我們首先對heap結構進行查詢實驗 --2.1通過dbcc ind發現有1個IAM page,1495個 data page dbcc ind(TESTDB3,test,-1); --2.2全表查詢,table scan,邏輯讀的次數是1495,Table 'test'. Scan count 1, logical reads 1495 select * from test --2.3條件查詢,table scan,邏輯讀次數是1495,Table 'test'. Scan count 1, logical reads 1495 select * from test where SalesOrderDetailID=55831 --3.接下來會在test2表上創建聚集索引和非聚集索引,然后查看創建索引以后的查詢效率。 --3.1在test2表的SalesOrderDetailID列上創建聚集索引 create clustered index idx_test2_SalesOrderDetailID on test2(SalesOrderDetailID); --3.2全表掃描,clustered index scan,所以也可以說,clustered index scan就是table scan。 --Table 'test2'. Scan count 1, logical reads 1513,邏輯多比原來多了,這是因為讀了索引頁 select * from test2 --條件查詢,Clustered index seek,Table 'test2'. Scan count 1, logical reads 4,發現邏輯讀大大減少 select * from test2 where SalesOrderDetailID=55831 --3.3查看test2表頁信息,發現有1個IAM page,5個index page,1507個data page,indexlevel有012三層。 TRUNCATE TABLE sp_table_pages; INSERT INTO sp_table_pages EXEC ('dbcc ind(TESTDB3,test2,-1)' );--(1513 row(s) affected),剛好是邏輯讀的數量 select * from sp_table_pages order by IndexLevel desc; select count(*) from sp_table_pages where PageType=1;--1507條記錄 --4.條件列是SalesOrderID,clustered index scan,Table 'test2'. Scan count 1, logical reads 1513 select * from test2 where SalesOrderID=55302 --4.1在SalsOrderID上面創建非聚集索引 create nonclustered index idx_test2_SalesOrderID on test2(SalesOrderID); --4.2再次查詢,execution plan是Key Lookup(Clustered)91%,Index Seek(NonClustered)9%。 --這說明是非聚集索引葉子節點找到了聚集索引的鍵值,然后通過聚集索引鍵值去查找記錄,也就是Key Lookup。 --Table 'test2'. Scan count 1, logical reads 44,邏輯讀大大減少。 select * from test2 where SalesOrderID=55302 --實驗3結束---------------------------------------------------------------------------
3.復合索引
數據庫中經常會存在復合索引,那么復合索引在什么情況下會起到查詢優化作用,又在什么情況下起不到作用呢。如果查詢條件是復合索引的非leading column,那么索引不起作用,不會使用這個復合索引。

--實驗4:復合索引,index seek,index scan --1.查詢的內容是索引的鍵,index seek,直接從非聚集索引的葉子節點返回,不需要再去查找聚集索引。 --Table 'test2'. Scan count 1, logical reads 2,邏輯讀只需要2次。 select SalesOrderID from test2 where SalesOrderID=55302 --2.查詢的內容大於索引的鍵,那么查詢計划是index seek+key lookup。Table 'test2'. Scan count 1, logical reads 44 select SalesOrderID,OrderQty from test2 where SalesOrderID=55302 --3.刪除在SalesOrderID列上的非聚集索引 DROP INDEX idx_test2_SalesOrderID ON test2; --4.創建復合索引 CREATE NONCLUSTERED INDEX IX_SalesOrderID_OrderQty ON test2(SalesOrderID,OrderQty); --4.1.where條件是復合索引的leading column,9%index seek+91%key lookup,logical reads 44 select * from test2 where SalesOrderID=55302 --4.2.where條件是復合索引的leading column跟second column,34%index seek+66%key lookup,logical reads 8 select * from test2 where SalesOrderID=55302 and OrderQty=2 --4.3.where條件是復合索引的second column跟leading column,同上 select * from test2 where OrderQty=2 and SalesOrderID=55302 --4.4.where條件是復合索引的second column,clustered index scan, logical reads 1513 select * from test2 where OrderQty=2 --5.在OrderQty列上創建單獨的非聚集索引 CREATE NONCLUSTERED INDEX IX_OrderQty ON test2(OrderQty); --5.1.logical reads 1513,clustered index scan,這是因為返回結果有14200 rows,當返回結果太大時查詢優化器會選擇不適用這個非聚集索引。 select * from test2 where OrderQty=2
PS:2012-9-3
今天看到了博客園中的數據庫查詢性能優化之利器—索引(二),看着覺得有點不對勁,所以對文中的疑點進行測試。
疑問一:一次查詢只能使用一個索引
參考:http://www.itpub.net/thread-1623492-1-1.html
首先我們准備實驗數據,在這里我新建一張OrderDetail2,並將adventureworks2008r2的 AdventureWorks2008R2.Sales.SalesOrderDetail表中的其中四列導入OrderDetail2表中,TSQL如下所示:

use TESTDB; --1.創建表 create table OrderDetail2 ( SalesOrderDetailID int, SalesOrderID int, CarrierTrackingNumber nvarchar(25), UnitPrice money NOT NULL, CONSTRAINT PK_SalesOrderDetailID PRIMARY KEY(SalesOrderDetailID) ); --2.插入數據 Insert into OrderDetail2(SalesOrderDetailID,SalesOrderID,CarrierTrackingNumber,UnitPrice) select SalesOrderDetailID,SalesOrderID,CarrierTrackingNumber,UnitPrice from AdventureWorks2008R2.Sales.SalesOrderDetail --3.創建單列非聚集索引 CREATE NONCLUSTERED INDEX idx_nc_SalesOrderID ON dbo.OrderDetail2(SalesOrderID); CREATE NONCLUSTERED INDEX idx_nc_CarrierTrackingNumber ON dbo.OrderDetail2(CarrierTrackingNumber); CREATE NONCLUSTERED INDEX idx_nc_UnitPrice ON dbo.OrderDetail2(UnitPrice); --5.開啟統計信息 set statistics io on set statistics time on set statistics profile on DBCC DROPCLEANBUFFERS--清空執行計划緩存 DBCC FREEPROCCACHE--清空數據緩存
然后我們按照UnitPrice來查詢,查詢語句如下:
select * from OrderDetail2 where UnitPrice =5.70
其查詢計划如下:
從上述查詢計划我們可以看出,一個查詢使用了兩個索引。在idx_nc_UnitPrice上面是哦那個了Index Seek,而在PK_SalesOrderDetailId上面使用了Clustered Index Seek。
疑問二:mutilindex(name,age,tel)。對於mutilindex,若判別條件為(name),(name,age),(name,age,tel)都可以使用該索引,而(name,tel),(age,tel),(tel)都不能夠使用該做引。
接下來我們創建一個復合索引包含SalesOrderID,CarrierTrackingNumber,UnitPrice這三個列,然后測試復合索引在什么情況下會被使用。創建復合索引的TSQL如下所示:

--4.創建復合索引 CREATE NONCLUSTERED INDEX idx_nc_com ON dbo.OrderDetail2(SalesOrderID,CarrierTrackingNumber,UnitPrice);
(1)然后我們將查詢條件設定為復合索引的引導列,我們會發現:where條件是引導列,不論查詢的是所有列或者是單列SalesOrderID,都使用了復合索引,而沒有使用單列索引。TSQL查詢如下所示:

--where條件是引導列,不論查詢的是所有列或者是單列SalesOrderID,都使用了復合索引,而沒有使用單列索引。 select * from OrderDetail2 where SalesOrderID = 43659 select SalesOrderID from OrderDetail2 where SalesOrderID = 43659
查詢計划如下圖所示:
(2)如果查詢條件是非引導列,那么將使用單列索引,而不使用復合索引,TSQL查詢如下所示,執行計划在疑問一中已經給出。

--CarrierTrackingNumber和UnitPrice是非引導列,所以使用自身的單列索引。與查詢結果的條目數無關 select * from OrderDetail2 where CarrierTrackingNumber = '4911-403C-98' select * from OrderDetail2 where UnitPrice=1.3282
(3)where查詢條件包含了引導列,那么不論引導列在where條件的何處(多條件情況),都會使用復合索引。

--不論引導列在where條件的何處位置,只要包含了引導列,那么就會使用復合索引。 --引導列緊跟 where select * from OrderDetail2 where SalesOrderID = 51702 and CarrierTrackingNumber='48F0-4F3E-AE' select * from OrderDetail2 where SalesOrderID = 51702 and UnitPrice=1.374 select * from OrderDetail2 where SalesOrderID = 51702 and CarrierTrackingNumber='48F0-4F3E-AE' and UnitPrice=1.374 --將引導列的位置放在后面 select * from OrderDetail2 where UnitPrice=1.374 and SalesOrderID = 51702
查詢計划如(1)所示。
(4)不包含引導列。假如where條件不包含引導列,那么將不會使用復合索引。比如執行如下TSQL查詢,就沒有使用復合索引,而是使用了兩個單列各自的非聚集索引。這又是一個“一個查詢可以使用多個索引”的例子。
select * from OrderDetail2 where CarrierTrackingNumber='48F0-4F3E-AE' and UnitPrice=1.374
上述查詢的查詢計划如下圖所示:
總結:對於符合復合mutilindex(name,age,tel)。若判別條件為(name),(name,age),(name,tel),(name,age,tel)都可以使用該復合索引,而(age,tel),(tel)都不能夠使用該做引。