當我談論索引時,大家經常會問我在複合非聚集索引里,列的順序是否重要?簡單來說:“看情況”。我們來具體看下為啥“看情況”…… 單例查找(Singleton Lookups) 當在你的表上有進行單例查找的查詢時,在複合非聚集索引里列的順序真的不重要。假設下列查詢: 現在你可以在StateProvince ...
當我談論索引時,大家經常會問我在複合非聚集索引里,列的順序是否重要?簡單來說:“看情況”。我們來具體看下為啥“看情況”……
單例查找(Singleton Lookups)
當在你的表上有進行單例查找的查詢時,在複合非聚集索引里列的順序真的不重要。假設下列查詢:
-- Without a supporting Non-Clustered Index we have to scan the complete Clustered Index SELECT AddressID FROM Person.Address WHERE StateProvinceID = 79 AND City = 'Bothell' GO
現在你可以在StateProvinceID和City,或City和StateProvinceID創建非聚集索引:
-- Create a supporting Non-Clustered Index CREATE NONCLUSTERED INDEX idx_Test ON Person.Address(StateProvinceID, City) GO -- SQL Server performs a Non-Clustered Index Seek operation in combination with a Seek Predicate SELECT AddressID FROM Person.Address WHERE StateProvinceID = 79 AND City = 'Bothell' GO -- Change the column ordering CREATE NONCLUSTERED INDEX idx_Test ON Person.Address(City, StateProvinceID) WITH (DROP_EXISTING = ON) GO -- The column ordering doesn't matter in the Non-Clustered Index SELECT AddressID FROM Person.Address WHERE StateProvinceID = 79 AND City = 'Bothell' GO
這裡非聚集索引里的列的順序真的不重要,因為SQL Server在執行計劃里直接進行非聚集索引查找操作(在與查找謂語集合里):
範圍掃描(Range Scans)
當我們討論在表上的範圍掃描時,這裡你檢索一組數據,就是另一回事了。假設你執行下列查詢:
SELECT AddressID FROM Person.Address WHERE StateProvinceID BETWEEN 10 AND 12 AND City = 'Bothell' GO
這次,支持的非聚集索引,你有2個方法:
- StateProvinceID和City列上的非聚集索引
- City和StateProvinceID列上的非聚集索引
我們先用第一個方法:
-- Create a supporting Non-Clustered Index CREATE NONCLUSTERED INDEX idx_Test ON Person.Address(StateProvinceID, City) GO
這個情況下,如你在執行計劃里所見,SQL在StateProvinceID列上對查詢進行非聚集索引查找操作,對於City列要計算剩餘謂語的值:
這真的不是個完美的執行計劃,因為你讀取了比你請求更多的數據。但有基於StateProvinceID列上的排序作為引導列, City作為隨後列,這是唯一可能的行為,如你從下圖所見:
現在我們嘗試交換下列來創建非聚集索引:City作為引導列,StateProvinceID作為第二列:
-- Change the column ordering in the Non-Clustered Index CREATE NONCLUSTERED INDEX idx_Test ON Person.Address(City, StateProvinceID) WITH (DROP_EXISTING = ON) GO -- Non-Clustered Index Seek on StateProvinceID *without* a Residual Predicate on column City SELECT AddressID FROM Person.Address WHERE StateProvinceID BETWEEN 10 AND 12 AND City = 'Bothell' GO
當你再次執行你的查詢,你會看到SQL Server再次執行了非聚集索引查找操作。但這次對於你的查詢,“沒有”剩餘謂語(Residual Predicate)。
因為你物理上讀取的剛好是你邏輯上請求的數據。但這個現在這麼可能呢?那就看看下麵的圖:在非聚集索引里數據是如何排序的:
如你所見,現在的數據按City預先排,在每個City組裡,你會有在StateProvinceID列的排序。因此你可以直接獲得邏輯請求的數據——不用進一步剩餘謂語(Residual Predicate)的值計算就可以返回值。
小結
當你要進行範圍掃描時——在複合非聚集索引里列的順序重要的!在多次交流會上我經常提到:SQL Server里的一切幾戶都與索引有關,索引本身就會預排序數據!沒別的!理解SQL Server是否可以直接查找邏輯請求的數據,你也需要在你的心中想象下如何使如何預排序的,你如何通過有效預排序數據來訪問它。
希望這篇文章可以讓你更好的理解在非聚集索引里,列排序如何影響查找操作。
感謝關註!