使用 FORCESEEK 資料表提示

FORCESEEK 資料表提示會強制查詢最佳化工具只使用索引搜尋作業,當做查詢中參考之資料表或檢視資料的存取路徑。您可以使用這個資料表提示來覆寫查詢最佳化工具所選擇的預設計畫,以免因為沒有效率的查詢計畫而產生效能問題。例如,如果計畫包含資料表或索引掃描運算子,而且對應的資料表在執行查詢時造成很高的讀取數,如 STATISTICS IO 輸出中所觀察到的結果所示,則強制索引搜尋作業可能會產生比較理想的查詢效能。尤其當不正確的基數或成本估計造成最佳化工具在計畫編譯時偏好掃描作業,此情況會特別明顯。

FORCESEEK 適用於叢集和非叢集的索引搜尋作業。可以針對 SELECT 陳述式的 FROM 子句中以及 UPDATE 或 DELETE 陳述式的 FROM <table_source> 子句中的任何資料表或檢視來指定它。

警告注意事項注意

由於 SQL Server 查詢最佳化工具通常會選取最好的查詢執行計畫,因此,我們建議資深的開發人員和資料庫管理員將它當做最後的解決辦法。

評估 FORCESEEK 適用性的查詢計畫

當查詢計畫在資料表或檢視上使用資料表或索引掃描運算子,但是索引搜尋運算子可能比較有效率時,FORCESEEK 資料表提示可能會很有用處。請考慮以下的查詢及後續執行計畫。

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d 
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

下列執行計畫顯示,查詢最佳化工具會選擇叢集索引掃描運算子來存取這兩個資料表中的資料。

具有叢集索引掃描運算子的執行計畫

您可以指定 FORCESEEK 提示 (如下列查詢所示),強制查詢最佳化工具在 Sales.SalesOrderDetail 資料表上執行搜尋作業。

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d WITH (FORCESEEK)
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

下列執行計畫會顯示在查詢中使用 FORCESEEK 提示的結果。會使用叢集索引搜尋作業來存取 Sales.SalesOrderDetail 資料表中的資料。

具有叢集索引搜尋運算子的執行計畫

索引聯集和交集支援

FORCESEEK 提示支援索引聯集和交集。此提示會讓查詢最佳化工具比較可能使用這些技術。為了避免讓簡單查詢的編譯時間減緩,通常會根據考量資料行基數和選擇性的規則來選擇索引聯集和交集。但是在指定 FORCESEEK 提示時,會略過這類規則,而且一定會考量這些技術。例如,假設有以下的查詢:

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 AND T.b = 2;

如果資料表 T 中的資料行 a 和 b 上有個別的非叢集索引,則可能會選擇索引交集計畫。也就是說,此計畫在資料行 a 上包含非叢集的索引搜尋作業以及在資料行 b 上包含非叢集的索引搜尋作業,並產生交集的索引鍵集合,然後再執行基底資料表的查閱作業。

在下列範例中,會選擇索引聯集計畫。也就是說,此計畫在資料行 a 上包含搜尋作業以及在資料行 b 上包含搜尋作業,並產生聯集的索引鍵集合,然後再執行基底資料表的查閱作業。

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 OR T.b = 2;

將 FORCESEEK 用於使用 LIKE 或 IN 的查詢

當查詢使用 IN 或 LIKE 當做搜尋述詞時,查詢最佳化工具規則及不良的基數估計也可能造成此最佳化工具執行資料表或索引掃描作業,而不是執行索引搜尋。

下列範例示範當使用 LIKE 或 IN 當做搜尋述詞時,FORCESEEK 提示如何強制查詢最佳化工具執行索引搜尋作業,而不是資料表掃描作業。若要檢視查詢執行計畫,請先按一下 [包括實際執行計畫] 工具列按鈕,然後再執行此範例。

USE tempdb;
GO
DROP TABLE t;
GO
CREATE TABLE t(i int UNIQUE, j int, vc varchar(100));
CREATE INDEX t_vc ON t(vc);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WITH (FORCESEEK) WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
SELECT * FROM t WHERE vc LIKE 'Test%';
GO
SELECT * FROM t WITH (FORCESEEK) WHERE vc LIKE 'Test%';
GO
DECLARE @vc varchar(100);
SELECT * FROM t WHERE vc LIKE @vc;
GO
DECLARE @vc varchar(100);
SELECT * FROM t WITH (FORCESEEK) where vc like @vc;
GO

在檢視上使用 FORCESEEK

指定 FORCESEEK 時,不一定需要索引提示。當您將 FORCESEEK 資料表提示套用到檢視或索引檢視表時,會將 FORCESEEK 提示遞迴地傳播到擴充版檢視的所有資料表中。如果有指定索引提示,會予以忽略。如果每一個基礎資料表沒有最起碼的一個索引,則不會找到任何計畫,而且會傳回 8622 錯誤。

當您在索引檢視的參考上一起使用 FORCESEEK 和 NOEXPAND 提示時,會使用此索引檢視而不先展開它。FORCESEEK 提示會直接套用到索引檢視表,並將它視為資料表。

如果您將 FORCESEEK 提示套用到資料表參考,則此資料表參考將無法參與索引檢視表比對。但是,不受 FORCESEEK 提示影響的其他查詢部分則可以參與索引檢視表比對。這可以對照搭配 INDEX 提示使用時的索引檢視表比對行為。

最佳作法考量

以下是建議的最佳作法:

  • 在使用 FORCESEEK 資料表提示之前,請確定資料庫上的統計資料是最新且正確的。

    藉由最新的統計資料,最佳化工具將可正確評估不同查詢計畫的成本並選擇高品質的計畫。因此,我們建議您最好針對每一個使用者資料庫將 AUTO_CREATE_STATISTICS 和 AUTO_UPDATE_STATISTICS 設定為 ON (預設值)。另外,您也可以使用 UPDATE STATISTICS 陳述式,手動更新資料表或檢視上的統計資料。

  • 評估查詢,找出可能會造成不良的基數或成本預估的項目,然後盡可能移除這些項目。例如,以參數或常值取代本機變數,並在查詢中限制多重陳述式資料表值函數和資料表變數的使用。如需有關要尋找之其他項目的詳細資訊,請參閱<Microsoft SQL Server 2005 查詢最佳化工具所使用的統計資料>(英文)。

  • 請勿搭配 FORCESEEK 使用 INDEX 提示,這是不必要的。也就是說,FORCESEEK 本身就會產生充足的計畫,如果同時也使用 INDEX 提示則可能會過度限制最佳化工具的選擇。此外,如果您變更資料表的實體結構描述來刪除提示中所指定的索引,則 INDEX 提示將會造成查詢失敗。對照之下,只要 FORCESEEK 提示套用所在的資料表上至少有一個可使用的索引,查詢就會編譯 (即使您變更索引結構)。

  • 請勿搭配 FORCESEEK 提示使用 INDEX 提示 INDEX (0)。INDEX (0) 會強制掃描基底資料表。搭配 FORCESEEK 使用時,不會找到任何計畫,而且會傳回 8622 錯誤。

  • 請勿搭配 FORCESEEK 提示使用 USE PLAN 查詢提示。如果您這樣做的話,會忽略 FORCESEEK 提示。