在上次<INDEX--從數據存放的角度看索引>中,我們說到"唯一非聚集索引"和“非唯一非聚集索引”在存儲上有一個明顯的差別:唯一非聚集索引的非葉子節點上不會包含RID的數據,讓我們繼續來深挖一下。
準備測試數據:
CREATE TABLE TB1( C1 INT, C2 INT, C3 INT)GOCREATE UNIQUE CLUSTERED INDEX IDX_C1 ON TB1(C1)GOCREATE UNIQUE INDEX IDX_C2 ON TB1(C2)GOCREATE INDEX IDX_C3 ON TB1(C3)GOINSERT INTO TB1(C1,C2,C3)VALUES(1,1,1)GOINSERT INTO TB1(C1,C2,C3)VALUES(2,2,2)GOINSERT INTO TB1(C1,C2,C3)VALUES(3,3,3)
索引編號如下:
再通過DBCC IND和DBCC PAGE來查看頁情況
唯一非聚集索引IDX_C2的數據頁:
非唯一非聚集索引IDX_C3的數據頁:
以上兩張圖有個明顯的區別是C1和C1(key),難道在“非唯一非聚集索引”中,“聚集索引鍵”也被放到“非聚集索引鍵”中并且參與排序啦?
相信很多DBA的朋友都遇到這樣的問題,要按照某些狀態值來查找數據,而這些狀態值是一個很小的集合(數量很小),如查找狀態值為1的最大訂單號
SELECT TOP(1)* FROM dbo.OrdersWHERE OrderState=1ORDER BY OrderID DESC
雖然OrderID為主鍵和唯一聚集索引,但按照OrderID來查找,可能需要進行大范圍CLUSTERED INDEX SEEK才能找到滿足條件OrderState=1的數據,因此盡管OrderState的可選擇性較低,我們還是會對其建立索引,那么問題來了?我們索引該建成什么樣呢?是建成:
CREATE INDEX IDX_OrderStateON dbo.Orders( OrderState)
還是建成:
CREATE INDEX IDX_OrderStateON dbo.Orders( OrderState, OrderID)
曾經我想當然地認為必須建成第二種方式,因為還需要對OrderID進行排序取TOP(1),但經過測試,神奇地發現兩種方式的效率一樣,無論“非唯一非聚集索引鍵”里有沒有包含“聚集索引鍵”,都會對“非唯一非聚集索引鍵”+“聚集索引鍵”進行排序。
思考這樣一個問題,假設對“非唯一非聚集索引鍵”,僅僅對其定義的鍵進行排序,如OrderState,而滿足OrderState=0的可能有1億數據,在進行數據更新的時候,首先更新聚集索引,并依次更新非聚集索引,更新索引數據首先要定位數據行才能更新,因此需要掃描這1億數據才能找到目標行,顯然這是不可接受的設計。
對于"唯一非聚集索引"來說,因為可以通過索引鍵便可以快速定位到索引數據行,且每個鍵值只會存在一行,因此失去了對“聚集索引鍵”進行排序的意義。
BTW, 也可以通過觀察相同鍵值下行位置(slotid)和插入順序來發現數據按照聚集索引鍵排序。
--===========================================================================
總結:
1. 對于“非唯一非聚集索引”,索引數據實際上是按照“非唯一非聚集索引鍵”+“聚集索引鍵”進行排序后存放的;
2. 對于“唯一非聚集索引”,索引數據實際上是按照“唯一非聚集索引鍵”進行排序后存放的;
3. 所有非聚集索引的葉子節點上都會存放RID的數據,但唯一非聚集索引的非葉子節點上不會包含RID的數據;
--===========================================================================
好好讀書。。。
新聞熱點
疑難解答