如果一張表上沒有聚集索引,數據將會隨機的順序存放在表里。以dbo.SalesOrderDetail_TEST為例子。它的上面沒有聚集索引,只有一個在SalesOrderID上的非聚集索引。所以表格的每一行記錄,不會按照任何順序,而是隨意地存放在Hash里。這個時候如果用戶想要找所有單價大於200的銷售詳細記錄,要運行的語句會是:
SET STATISTICS PROFILE ON SELECT SalesOrderDetailID , Unitprice FROM SalesOrderDetail_test WHERE UnitPrice > 200
由於表在UnitPrice上沒有索引,所以SQL SERVER不得不對這個表從頭到尾掃描一遍,把所有UnitPrice的值大於200的記錄一個一個挑出來。
從執行計划里可以清楚地看出來SQL SERVER 這里做了一個表掃描(下圖),后面會詳細介紹如何得到和分析執行計划
如果這個表上有聚集索引,事情會怎么樣呢?還是剛才那張表做例子,先給它的值是唯一的字段 Unitprice上建立一個聚集索引。這樣所有的數據都會按照聚集索引的順序存儲。
CREATE CLUSTERED INDEX SalesOrderDetail_TEST_CL ON dbo.SalesOrderDetail_test (SalesOrderDetailID)
可惜的是,查詢條件Unitprice上沒有索引,所以SQL SERVER還是要把所有記錄都掃描一遍。如下圖
與之前不同的是,執行計划里的表掃描變成了聚集索引掃描。因為在有聚集索引的表上,數據是直接存放在索引的最底層的,所以要掃描整個表格的數據,就是把整個聚集索引掃描一遍。在這里,聚集索引掃描就相當於一個表掃描。所要用的時間和資源與表掃描沒有什么差別。並不是說這里有了”Index”這個字樣,就說明執行計划比表掃描的有多大進步。當然反過來講,如果看到”Table Scan”的字樣,就說明這個表格上沒有聚集索引。
現在在 UnitPrice 上面建一個非聚集索引,看看情況會有什么變化
CREATE NONCLUSTERED INDEX SalesOrderDetail_TEST_NCL_Price ON dbo.SalesOrderDetail_test (UnitPrice)
再次查詢
SET STATISTICS PROFILE ON SELECT SalesOrderDetailID , Unitprice FROM SalesOrderDetail_test WHERE UnitPrice > 200
在非聚集索引里,會為每條記錄存儲一份非聚集索引索引鍵的值和一份聚集索引索引鍵的值(在沒有聚集索引的表格里,是RID值)。所以在這里,每條記錄都會有一份 SalesOrderDetailID和UnitPrice記錄,按照UnitPrice的順序存放。再查詢,就會看到這次的SQL SERVER不是掃描整個表,會根據新建的索引直接找到符合的記錄的值。
但是光用UnitPrice建立在上的索引不能告訴我們其它字段的值。如果在剛才那個查詢里再增加幾個字段返回,SQL SERVER 就要先在非聚集索引上找到所有UnitPrice大於200的記錄,然后再根據SalesOrderDetailID的值找到存儲在聚集索引上的詳細數據。這個過程可以稱為 “Bookmark Loopup”
SET STATISTICS PROFILE ON SELECT SalesOrderId,SalesOrderDetailID , Unitprice FROM SalesOrderDetail_test with(index = SalesOrderDetail_TEST_NCL_Price) WHERE UnitPrice > 200
在SQL SERVER 2005以后,Bookmark Loopup 的動作用一個嵌套循環來完成。所以在執行計划里,可以看到SQL SERVR是先SEEK了非聚集索引,然后再用Clustered Index Seek 把需要的行找出來。這里的嵌套循環其實就是 Bookmark Loopup 如下圖。
注:Bookmark Loopup就是聚集索引
在SQL SERVER里根據數據找尋目標的不同和方法不同。有下面幾種情況。
結構 |
Scan |
Seek |
堆(沒有聚集索引的表) |
Tablescan |
無 |
聚集索引 |
Clustered Index Scan |
Clustered Index Seek |
非聚集索引 |
Index Scan |
Index Seek |
如果在執行計划里看到這些動作,就應該能夠知道SQL SERVER正在對哪種對象在做什么樣的操作。表掃描表明正在處理的表沒有聚集索引,SQL SERVER正在掃描整張表。聚集索引掃描表明SQL SERVER正在掃描一張有聚集索引的表,但是也會是整表掃描。Index Scan表明SQL SERVER正在掃描一個非聚集索引。由於非聚集索引上一般只會有一小部分字段,所以這些雖然也是掃描,但是代價會比整表掃描要小很多。Clustered Index Seek 和Index Seek會比Scan 說明SQL SERVER正在利用索引結果檢索目標數據。如果結果集只占表格總數據量的一小部分,Seek 會比Scan便宜很多,索引就起到了提高性能的作用。
了解這些是為以后讀懂執行計划做基礎。水平有限,暫時為這些吧。大家可以多多交流。
下一次會寫統計信息的東西,可能會稍多一些。上述一語句均為上一次發博客的腳本為例。