篩選索引--filter indexs


篩選索引是一種經過優化的非聚集索引,尤其適用於涵蓋從定義完善的數據子集中選擇數據的查詢。篩選索引使用篩選謂詞對表中的部分行進行索引。

一:篩選索引與全表索引相比具有以下優點

1.提高了查詢性能和計划質量
設計良好的篩選索引可以提高查詢性能和執行計划質量,因為它比全表非聚集索引小並且具有經過篩選的統計信息。與全表統計信息相比,經過篩選的統計信息更加准確,因為它們只涵蓋篩選索引中的行。

2.減少了索引維護開銷
僅在數據操作語言 (DML) 語句對索引中的數據產生影響時,才對索引進行維護。與全表非聚集索引相比,篩選索引減少了索引維護開銷,因為它更小並且僅在對索引中的數據產生影響時才進行維護。篩選索引的數量可以非常多,特別是在其中包含很少受影響的數據時。同樣,如果篩選索引只包含頻繁受影響的數據,則索引大小較小時可以減少更新統計信息的開銷。

3.減少了索引存儲開銷
在沒必要創建全表索引時,創建篩選索引可以減少非聚集索引的磁盤存儲開銷。可以使用多個篩選索引替換一個全表非聚集索引而不會明顯增加存儲需要。

4.設計注意事項
為了設計有效的篩選索引,必須了解應用程序使用哪些查詢以及這些查詢與您的數據子集有何關聯。例如,所含值中大部分為 NULL 的列、含異類類別的值的列以及含不同范圍的值的列都屬於具有定義完善的子集的數據。以下設計注意事項提供了篩選索引優於全表索引的各種情況。

5.數據子集的篩選索引
在列中只有少量相關值需要查詢時,可以針對值的子集創建篩選索引。例如,當列中的值大部分為 NULL 並且查詢只從非 NULL 值中進行選擇時,可以為非 NULL 數據行創建篩選索引。由此得到的索引與對相同鍵列定義的全表非聚集索引相比,前者更小且維護開銷更低。

eg1:例如,AdventureWorks 數據庫中有一個包含 2679 行的 Production.BillOfMaterials 表。EndDate 列只有 199 行包含非 NULL 值,其余 2480 行均包含 NULL。下面的篩選索引將涵蓋這樣的查詢:返回在此索引中定義的列的查詢,以及只選擇 EndDate 值不為 NULL 的行的查詢。


USE AdventureWorks;
GO
IF EXISTS (SELECT name FROM sys.indexes
WHERE name = N'FIBillOfMaterialsWithEndDate'
AND object_id = OBJECT_ID (N'Production.BillOfMaterials'))
DROP INDEX FIBillOfMaterialsWithEndDate
ON Production.BillOfMaterials
GO
CREATE NONCLUSTERED INDEX FIBillOfMaterialsWithEndDate
ON Production.BillOfMaterials (ComponentID, StartDate)
WHERE EndDate IS NOT NULL;
GO
篩選索引 FIBillOfMaterialsWithEndDate 對下面的查詢有效。您可以顯示查詢執行計划,以確定查詢優化器是否使用了此篩選索引。有關如何顯示查詢執行計划的信息,請參閱分析查詢。


SELECT ProductAssemblyID, ComponentID, StartDate
FROM Production.BillOfMaterials
WHERE EndDate IS NOT NULL;
GO有關如何創建篩選索引以及如何定義篩選索引謂詞表達式的詳細信息,請參閱 CREATE INDEX (Transact-SQL)。

6.異類數據的篩選索引
表中含有異類數據行時,可以為一種或多種類別的數據創建篩選索引。

eg2:例如,Production.Product 表中列出的每種產品均分配到一個 ProductSubcategoryID,后者又與 Bikes、Components、Clothing 或 Accessories 產品類別關聯。這些類別為異類類別,因為它們在 Production.Product 表中的列值並不是緊密相關的。例如,對於每種產品類別,Color、ReorderPoint、ListPrice、Weight、Class 和 Style. 均具有唯一特征。假設會經常查詢具有子類別 27-36 的 Accessories。通過對 Accessories 子類別創建篩選索引,可以提高對 Accessories 的查詢的性能。

下面的示例對 Production.Product 表中 Accessories 子類別中的所有產品創建一個篩選索引。


USE AdventureWorks;
GO
IF EXISTS (SELECT name FROM sys.indexes
WHERE name = N'FIProductAccessories'
AND object_id = OBJECT_ID ('Production.Product'))
DROP INDEX FIProductAccessories
ON Production.Product;
GO
CREATE NONCLUSTERED INDEX FIProductAccessories
ON Production.Product (Name, ListPrice)
WHERE ProductSubcategoryID >= 27 AND ProductSubcategoryID <= 36;
GO
篩選索引 FIProductAccessories 對下面的查詢有效,因為查詢謂詞等效於篩選索引表達式。

SELECT Name, ListPrice
FROM Production.Product
WHERE ProductSubcategoryID BETWEEN 27 AND 36;
GO

7.視圖與篩選索引
視圖是存儲查詢定義的虛擬表;與篩選索引相比,其用途更廣,功能更強。有關視圖的詳細信息,請參閱了解視圖和使用視圖的情況。下表比較了在視圖和篩選索引中可以使用的部分功能。

在表達式中允許             視圖                     篩選的索引
計算列                           是                             否

聯接                              是                             否

多個表                           是                             否

謂詞中的簡單比較邏輯        是                             是

謂詞中的復雜邏輯             是                              是



*有關謂詞中的簡單比較邏輯,請參閱 CREATE INDEX 中的 WHERE 子句語法。

**有關謂詞中的復雜比較邏輯,請參閱 SELECT 中的 WHERE 子句語法。

8.不能對視圖創建篩選索引。但是,查詢優化器可以從對視圖中引用的表定義的篩選索引中獲益。對於從視圖中選擇數據的查詢,如果查詢結果正確,查詢優化器會考慮對此查詢使用篩選索引。下面的示例創建一個視圖(開始日期在 2000 年 4 月 1 日以后)和一個篩選索引(開始日期在 2000 年 8 月 1 日以后)。

USE AdventureWorks;
GO
IF OBJECT_ID ('ViewOnBillOfMaterials') IS NOT NULL
DROP VIEW ViewOnBillOfMaterials;
GO
CREATE VIEW ViewOnBillOfMaterials AS
SELECT ComponentID, StartDate, EndDate, StartDate + 2 AS ShipDate
FROM Production.BillOfMaterials
WHERE StartDate > '20000401';
GO
IF EXISTS (SELECT name FROM sys.indexes
WHERE name = N'FIBillOfMaterialsByStartDate'
AND object_ID = OBJECT_ID (N'Production.BillOfMaterials'))
DROP INDEX FIBillOfMaterialsByStartDate
ON Production.BillOfMaterials;
GO
CREATE NONCLUSTERED INDEX FIBillOfMaterialsByStartDate
ON Production.BillOfMaterials (ComponentID, StartDate, EndDate)
WHERE StartDate > '20000801';
GO
在下面的示例中,查詢選擇 2000 年 9 月 1 日以后的開始日期,這些日期完全包含在此篩選索引和篩選視圖中。查詢優化器將考慮使用篩選索引 FIBillOfMaterialsByStartDate,因為其中包含了正確的查詢結果。

SELECT StartDate, ComponentID FROM ViewOnBillOfMaterials
WHERE StartDate > '20000901';
GO

在下一示例中,查詢選擇 2000 年 6 月 1 日以后的開始日期,這些日期完全包含在此視圖中,但未完全包含在此篩選索引中。查詢優化器不考慮使用篩選索引 FIBillOfMaterialsByStartDate,因為與查詢從視圖中選擇數據所返回的正確結果相比,查詢使用篩選索引會返回不同的結果。

SELECT StartDate, ComponentID FROM ViewOnBillOfMaterials
WHERE StartDate > '20000601';
GO

9.索引視圖與篩選的索引的對比

篩選的索引與索引視圖相比,具有以下優點:

減少了索引維護開銷。例如,相對於索引視圖而言,查詢處理器使用更少的 CPU 資源便可更新篩選的索引。

改善了計划質量。例如,在查詢編譯期間,查詢優化器考慮使用篩選的索引的情況要比考慮使用等效的索引視圖的情況多。

聯機索引重新生成。您可以在篩選的索引可用於查詢時重新生成它們。索引視圖不支持聯機索引重新生成。有關詳細信息,請參閱 ALTER INDEX (Transact-SQL) 的 REBUILD 選項。

非唯一索引。篩選的索引可以是非唯一的,而索引視圖必須是唯一的。

出於以上原因,建議盡可能使用篩選的索引,不使用索引視圖。如果滿足以下條件,則可以使用篩選的索引而不使用索引視圖:視圖只引用一個表,查詢不返回計算列且視圖謂詞使用簡單的比較邏輯。例如,允許在視圖定義中使用如下謂詞表達式,但不允許在篩選索引中使用它,因為它包含 LIKE 運算符。

WHERE StartDate > '20000701' AND ModifiedDate LIKE 'E%'

鍵列
最好在篩選索引定義中包含少量的鍵或包含列,並且只包含查詢優化器為查詢執行計划選擇篩選索引所需的列。無論某一篩選索引是否涵蓋了查詢,查詢優化器都可以為查詢選擇此篩選索引。但是,如果某一篩選索引涵蓋了查詢,則查詢優化器更有可能選擇此篩選索引。有關涵蓋查詢的詳細信息,請參閱創建帶有包含列的索引。

在某些情況下,篩選索引涵蓋查詢,但沒有將篩選索引表達式中的列作為鍵或包含列包括在篩選索引定義中。以下准則說明了篩選索引表達式中的列何時應為篩選索引定義中的鍵或包含列。這些示例引用了此前創建的篩選索引 FIBillOfMaterialsWithEndDate。

如果篩選索引表達式等效於查詢謂詞並且查詢並未在查詢結果中返回篩選索引表達式中的列,則篩選索引表達式中的列不需要作為篩選索引定義中的鍵或包含列。例如,FIBillOfMaterialsWithEndDate 涵蓋下面的查詢,因為查詢謂詞等效於篩選表達式,並且查詢結果中未返回 EndDate。FIBillOfMaterialsWithEndDate 不需要將 EndDate 作為篩選索引定義中的鍵或包含列。

SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WHERE EndDate IS NOT NULL;
GO

如果查詢謂詞在不與篩選索引表達式等效的比較中使用了篩選索引表達式中的某列,則該列應為篩選索引定義中的鍵或包含列。例如,FIBillOfMaterialsWithEndDate 對下面的查詢有效,因為它從篩選索引中選擇了行的子集。但是,它不涵蓋下面的查詢,因為在比較 EndDate > '20000101' 中使用了 EndDate,此比較不與篩選索引表達式等效。查詢處理器在不查找 EndDate 值的情況下無法執行此查詢。因此,EndDate 應為篩選索引定義中的鍵或包含列。

SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WHERE EndDate > '20000101';
GO

如果篩選索引表達式中的某列在查詢結果集中,則該列應為篩選索引定義中的鍵或包含列。例如,FIBillOfMaterialsWithEndDate 不涵蓋下面的查詢,因為它在查詢結果中返回了 EndDate 列。因此,EndDate 應為篩選索引定義中的鍵或包含列。

SELECT ComponentID, StartDate, EndDate FROM Production.BillOfMaterials
WHERE EndDate IS NOT NULL;
GO

表的主鍵不需要是篩選索引定義中的鍵或包含列。主鍵自動包含在所有非聚集索引(包括篩選索引)中。

10.篩選謂詞中的數據轉換運算符
如果篩選索引結果的篩選索引表達式中指定的比較運算符會導致隱式或顯式數據轉換,則轉換發生在比較運算符的左邊時,會出現錯誤。解決方法是在比較運算符的右邊編寫包含數據轉換運算符(CAST 或 CONVERT)的篩選索引表達式。

下面的示例創建一個包含多種數據類型的表。

USE AdventureWorks;
GO
IF OBJECT_ID ('dbo.TestTable') IS NOT NULL
DROP TABLE dbo.TestTable;
GO
CREATE TABLE TestTable (a int, b varbinary(4));
GO
在下面的篩選索引定義中,列 b 隱式轉換為整數數據類型,以便與常量 1 進行比較。因為轉換發生在篩選謂詞中運算符的左邊,所以這會生成錯誤消息 10611。

USE AdventureWorks;
GO
IF EXISTS ( SELECT name from sys.indexes
WHERE name = N'TestTabIndex'
AND object_id = OBJECT_ID (N'dbo.TestTable'))
DROP INDEX TestTabIndex on dbo.TestTable
GO
CREATE NONCLUSTERED INDEX TestTabIndex ON dbo.TestTable(a,b)
WHERE b = 1;
GO

解決方法是將右側的常量轉換為與列 b 的類型相同的類型,如下例所示:

CREATE INDEX TestTabIndex ON dbo.TestTable(a,b)
WHERE b = CONVERT(Varbinary(4), 1);
GO

將數據轉換從比較運算符的左邊移動到右邊可能會改變轉換的含義。在上例中,將 CONVERT 運算符添加到右邊時,相應的比較從整數比較更改為 varbinary 比較。

11.引用依賴項
sys.sql_expression_dependencies 目錄視圖將篩選索引表達式中的每一列作為一個引用依賴項進行跟蹤。不能刪除、重命名或更改在篩選索引表達式中定義的表列的定義。

12.何時使用篩選索引
列中包含查詢在 SELECT 語句中引用的定義完善的數據子集時,篩選索引很有用。以下是一些示例:

1)僅包含少量非 NULL 值的稀疏列。

2)包含多種類別的數據的異類列。

3)包含多個范圍的值(如美元金額、時間和日期)的列。

4)由列值的簡單比較邏輯定義的表分區。

5)如果索引中的行數與全表索引相比較少時,篩選索引減少的維護開銷最為明顯。如果篩選索引包含表中的大部分行,則與全表索引相比,其維護開銷可能更高。在這種情況下,應使用全表索引而不是篩選索引。

6)篩選索引是針對一個表定義的,僅支持簡單比較運算符。如果需要引用多個表或具有復雜邏輯的篩選表達式,則應創建視圖。

13.篩選索引功能支持
一般情況下,數據庫引擎和工具為篩選索引提供了與非聚集全表索引相同的支持,將篩選索引視為特殊類型的非聚集索引。下面的列表提供了有關對篩選索引提供完全支持、不提供支持或提供有限支持的工具和功能的說明。

ALTER INDEX 支持篩選索引。若要修改篩選索引表達式,請使用 CREATE INDEX WITH DROP_EXISTING。

1)缺失索引功能不建議使用篩選索引。

2)數據庫引擎優化顧問在提供索引優化建議時會考慮篩選索引。

3)聯機索引操作支持篩選索引。

4)表提示支持篩選索引,但有一些不適用於非篩選索引的限制。下一節將介紹這些內容。

14.查詢注意事項
如果不論是否使用篩選索引,查詢均選擇相同的結果,則查詢優化器會使用篩選索引。此前介紹的篩選索引 FIBillOfMaterialsWithEndDate 對以下兩個查詢有效。在第一個示例中,查詢謂詞與篩選索引謂詞 WHERE EndDate IS NOT NULL 完全匹配。在第二個示例中,由於查詢謂詞包含索引中行的子集,所以它比篩選謂詞具有更強的選擇性。

SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WHERE EndDate IS NOT NULL;
GO
SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WHERE EndDate < '20000701';
GO

下一個查詢也可使用 FIBillOfMaterialsWithEndDate。但是,由於存在其他決定查詢開銷的因素(如查詢謂詞的選擇性),優化器可能不會選擇篩選索引。如下例所示,可以通過將篩選索引用作查詢提示強制優化器選擇篩選索引。

SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WITH ( INDEX ( FIBillOfMaterialsWithEndDate ) )
WHERE EndDate IN ('20000825', '20000908', '20000918');
GO

如果查詢可以返回不在篩選索引中的行,則查詢優化器將不會使用篩選索引。例如,查詢優化器將不考慮對下面的查詢使用 FIBillOfMaterialsWithEndDate,因為查詢可能返回 NULL EndDate 值和非 NULL ModifiedDate 值,這些值不能包含在 FIBillOfMaterialsWithEndDate 中(因為它只包含非 NULL EndDate 值)。

SELECT ComponentID, StartDate FROM Production.BillOfMaterials
WHERE EndDate IS NOT NULL OR ModifiedDate IS NOT NULL;
GO

如果將篩選索引顯式用作表提示並且篩選索引可能不包含所有查詢結果,則查詢優化器將生成查詢編譯錯誤 8622。在下面的示例中,查詢優化器會生成錯誤 8622,因為 FIBillOfMaterialsWithEndDate 對此查詢無效,並且它顯式用作了索引提示:

SELECT StartDate, ComponentID FROM Production.BillOfMaterials
WITH ( INDEX ( FIBillOfMaterialsWithEndDate ) )
WHERE EndDate IS NOT NULL OR ModifiedDate IS NOT NULL;
GO

15.參數化查詢
在某些情況下,參數化查詢在編譯時包含的信息不足以滿足查詢優化器選擇篩選索引的需要。可能可以重寫此查詢以提供缺少的信息。在下面的示例中,查詢優化器不考慮對 SELECT 語句使用篩選索引 FIBillOfMaterialsWithComponentID,因為 @p 和 @q 的參數值在編譯時未知。下面的查詢示例運行時將 SHOWPLAN_XML 設置為 ON,以使您可以在 SHOWPLAN_XML 輸出中查看參數化查詢的不匹配的篩選索引。

USE AdventureWorks;
GO
IF EXISTS ( SELECT name FROM sys.indexes
WHERE name = N'FIBillOfMaterialsWithComponentID'
AND object_id = OBJECT_ID (N'Production.BillOfMaterials'))
DROP INDEX FIBillOfMaterialsWithComponentID
ON Production.BillOfMaterials;
GO
CREATE NONCLUSTERED INDEX FIBillOfMaterialsWithComponentID
ON Production.BillOfMaterials (ComponentID, StartDate, EndDate)
WHERE ComponentID IN (533, 324, 753);
GO
SET SHOWPLAN_XML ON;
GO
DECLARE @p AS INT, @q AS INT;
SET @p = 533;
SET @q = 324;
SELECT StartDate, ComponentID from Production.BillOfMaterials
WHERE ComponentID = @p OR ComponentID = @q;
GO
SET SHOWPLAN_XML OFF;
GO
SHOWPLAN_XML 輸出中的 UnmatchedIndexes 元素和 Parameterization 子元素指示篩選索引與查詢不匹配。有關如何查看 SHOWPLAN_XML 輸出的信息,請參閱 XML 顯示計划。

解決方法是修改此查詢,使在參數化表達式不是篩選謂詞的子集時查詢結果為空。下面的查詢說明了如何進行這種修改。通過將 ComponentID in (533, 324, 753) 表達式添加到 WHERE 子句,確保了此查詢的結果是篩選謂詞表達式的子集。通過這種修改,查詢優化器可以考慮對下面的 SELECT 語句使用篩選索引 FIBillOfMaterialsWithComponentID。

USE AdventureWorks;
GO
SET SHOWPLAN_XML ON;
GO
DECLARE @p AS INT, @q AS INT;
SET @p = 533;
SET @q = 324;
SELECT StartDate, ComponentID FROM Production.BillOfMaterials
WHERE ComponentID in (533, 324, 753)
AND (ComponentID = @p OR ComponentID = @q);
GO
SET SHOWPLAN_XML OFF;
GO

16.簡單參數化
在大多數情況下,如果某查詢計划包括篩選索引,查詢優化器將不對該查詢執行簡單參數化(在 SQL Server 2005 中稱為“自動參數化”)。對此類查詢執行簡單參數化可擴大可能參數值的范圍,這樣,篩選索引便不能保證查詢結果的准確性。例如,如果 SELECT 語句的 WHERE 子句使用了在篩選索引的謂詞中使用的列,則查詢優化器可能不會執行簡單參數化,這是因為查詢計划中很可能會包括篩選索引。

如果適合,使用本節中所述的准則重寫查詢以確保篩選索引將涵蓋該查詢,也許能夠參數化該查詢。

17.使用鍵查找的查詢
查詢優化器可以執行鍵查找操作來檢索篩選索引沒有涵蓋的剩余列,從而即使在某篩選索引不涵蓋查詢的情況下也可以使用該篩選索引。有關鍵查找的詳細信息,請參閱Key Lookup Showplan 運算符。如果估計的鍵查找次數很少,則查詢優化器可能會選擇此方法。下面的查詢使用索引提示強制查詢處理器使用 FIBillOfMaterialsWithEndDate,同時對 EndDate 執行書簽查找操作。對於查詢謂詞中的 EndDate > @date 比較,會執行鍵查找操作。

USE AdventureWorks;
GO
DECLARE @date AS DATE;
SET @date = '20000825'
SELECT ComponentID, StartDate, EndDate FROM Production.BillOfMaterials
WITH ( INDEX (FIBillOfMaterialsWithEndDate) )
WHERE EndDate > @date;
GO請注意,EndDate > @Date 與篩選索引表達式 EndDate IS NOT NULL 不完全匹配。篩選索引對此參數化查詢仍有效,因為它返回了由篩選索引表達式定義的行的子集。

轉載:http://space.itpub.net/?uid-16436858-action-viewspace-itemid-515883


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM