查詢提示 (Transact-SQL)
查詢提示會指定所指出的提示應該用於整個查詢。查詢提示會影響陳述式中的所有運算子。如果主要查詢涉及 UNION,只有最後一個包含 UNION 作業的查詢可以有 OPTION 子句。查詢提示指定在 OPTION 子句中。如果一個或多個查詢提示造成查詢最佳化工具不會產生有效的計畫,就會產生 8622 錯誤。
警告 |
---|
由於 SQL Server 查詢最佳化工具通常會選取最好的查詢執行計畫,因此,我們建議資深的開發人員和資料庫管理員將它當做最後的解決辦法。 |
適用於:
語法
<query_hint > ::=
{ { HASH | ORDER } GROUP
| { CONCAT | HASH | MERGE } UNION
| { LOOP | MERGE | HASH } JOIN
| FAST number_rows
| FORCE ORDER
| MAXDOP number_of_processors
| OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )
| OPTIMIZE FOR UNKNOWN
| PARAMETERIZATION { SIMPLE | FORCED }
| RECOMPILE
| ROBUST PLAN
| KEEP PLAN
| KEEPFIXED PLAN
| EXPAND VIEWS
| MAXRECURSION number
| USE PLAN N'xml_plan'| TABLE HINT ( exposed_object_name [ , <table_hint> [ [, ]...n ] ] )<table_hint> ::=
[ NOEXPAND ] {
INDEX (index_value [ ,...n ] ) | INDEX = (index_value)
| FASTFIRSTROW
| FORCESEEK
| HOLDLOCK
| NOLOCK
| NOWAIT
| PAGLOCK
| READCOMMITTED
| READCOMMITTEDLOCK
| READPAST
| READUNCOMMITTED
| REPEATABLEREAD
| ROWLOCK
| SERIALIZABLE
| TABLOCK
| TABLOCKX
| UPDLOCK
| XLOCK
}
引數
{ HASH | ORDER } GROUP
指定查詢的 GROUP BY、DISTINCT 或 COMPUTE 子句所說明的彙總應該使用雜湊或排序。{ MERGE | HASH | CONCAT } UNION
指定所有 UNION 作業都是藉由合併、雜湊或串連各個 UNION 集來執行的。如果指定了多個 UNION 提示,查詢最佳化工具會從指定的提示中,選取成本最低的策略。{ LOOP | MERGE | HASH } JOIN
指定所有聯結作業都是由整個查詢中的 LOOP JOIN、MERGE JOIN 或 HASH JOIN 來執行的。如果指定了多個聯結提示,最佳化工具會從允許使用的聯結提示中,選取成本最低的聯結策略。如果在相同查詢中,也在 FROM 子句中指定了一組特定資料表的聯結提示,在聯結兩份資料表時,雖然仍必須遵照查詢提示,但這個聯結提示優先。因此,這組資料表的聯結提示可能只會限制查詢提示中允許使用之聯結方法的選取。如需詳細資訊,請參閱<聯結提示 (Transact-SQL)>。
FAST number_rows
指定將查詢最佳化,以快速擷取第一個 number_rows。這是一個非負數整數。在傳回第一個 number_rows 之後,查詢會繼續執行,且會產生它的完整結果集。FORCE ORDER
指定在查詢最佳化期間,保留查詢語法所指出的聯結順序。使用 FORCE ORDER 不會影響查詢最佳化工具所可能有的角色反轉行為。如需詳細資訊,請參閱<瞭解雜湊聯結>。[!附註]
在 MERGE 陳述式中,除非指定了 WHEN SOURCE NOT MATCHED 子句,否則就會根據聯結順序,先存取來源資料表,然後再存取目標資料表。指定 FORCE ORDER 可以保留此預設行為。
如需有關在查詢包含檢視表時,SQL Server 查詢最佳化工具如何強制執行 FORCE ORDER 提示的資訊,請參閱<檢視解析>。
MAXDOP number
針對指定這個選項的查詢覆寫 sp_configure 和資源管理員的 max degree of parallelism 組態選項。MAXDOP 查詢提示可能會超過使用 sp_configure 所設定的值。如果 MAXDOP 超過使用資源管理員所設定的值,Database Engine 就會使用<ALTER WORKLOAD GROUP (Transact-SQL)>中所描述的資源管理員 MAXDOP 值。當您使用 MAXDOP 查詢提示時,適用所有搭配 max degree of parallelism 組態選項使用的語意規則。如需詳細資訊,請參閱<max degree of parallelism 選項>。警告 如果 MAXDOP 設定為零,則伺服器會選擇平行處理原則的最大程度。伺服器在內部會將 DOP 頂端設定為 64。
OPTIMIZE FOR ( @variable\_name { UNKNOWN | = literal_constant } [ , ...n] )
指示查詢最佳化工具在查詢進行編譯和最佳化時,使用特定的本機變數值。只有在查詢最佳化期間,才使用這個值,在查詢執行期間,不使用這個值。@variable\_name
這是查詢所用之本機變數的名稱,您可以指派這個本機變數的值來搭配使用 OPTIMIZE FOR 查詢提示。UNKNOWN
指定查詢最佳化工具使用統計資料 (而非初始值) 來判斷查詢最佳化期間的區域變數值。literal_constant
這是將指派給 @variable\_name,以便搭配 OPTIMIZE FOR 查詢提示使用的常值常數值。只有在查詢最佳化期間,才使用 literal_constant,在查詢執行期間,不能用來做為 @variable\_name 的值。literal_constant 可以是任何能夠用常值常數來表示的 SQL Server 系統資料類型。literal_constant 的資料類型必須可以隱含地轉換為 @variable\_name 在查詢中參考的資料類型。
OPTIMIZE FOR 可以抵銷最佳化工具的預設參數偵測行為,當您建立計畫指南時,也可以使用它。如需詳細資訊,請參閱<重新編譯預存程序>和<使用計畫指南對已部署應用程式中的查詢進行最佳化>。
OPTIMIZE FOR UNKNOWN
指示查詢最佳化工具在編譯及最佳化查詢時,將統計資料 (而非初始值) 用於所有的區域變數,包括以強制參數化所建立的參數。如需有關強制參數化的詳細資訊,請參閱<強制參數化>。如果 OPTIMIZE FOR @variable\_name = literal_constant 而且在相同的查詢提示中使用 OPTIMIZE FOR UNKNOWN,查詢最佳化工具會將指定的 literal_constant 用於特定值,並將 UNKNOWN 用於其餘的變數值。只有在查詢最佳化期間才使用這些值,查詢執行期間則不使用這些值。
PARAMETERIZATION { SIMPLE | FORCED }
指定 SQL Server 查詢最佳化工具在查詢完成時套用在查詢的參數化規則。重要事項 PARAMETERIZATION 查詢提示只能指定在計畫指南內。您不能在查詢中直接指定它。
SIMPLE 指示查詢最佳化工具嘗試簡單參數化。FORCED 指示最佳化工具嘗試強制參數化。PARAMETERIZATION 查詢提示用來覆寫計畫指南內 PARAMETERIZATION 資料庫 SET 選項目前的設定。如需詳細資訊,請參閱<使用計畫指南指定查詢參數化行為>。
RECOMPILE
指示 SQL Server Database Engine 捨棄在執行查詢之後所產生的計畫,強制查詢最佳化工具在下次執行相同的查詢時,重新編譯查詢計畫。在未指定 RECOMPILE 的情況下,Database Engine 會快取查詢計畫和重複使用它們。當編譯查詢計畫時,RECOMPILE 查詢提示會使用查詢中任何本機變數目前的值,如果查詢在預存程序內,就會將目前的值傳給任何參數。當不必編譯整個預存程序,只需要重新編譯預存程序內的部分查詢時,RECOMPILE 是非常有用的替代方案,可供您建立使用 WITH RECOMPILE 子句的預存程序。如需詳細資訊,請參閱<重新編譯預存程序>。另外,當您建立計畫指南時,RECOMPILE 也非常有用。如需詳細資訊,請參閱<使用計畫指南對已部署應用程式中的查詢進行最佳化>。
ROBUST PLAN
強制查詢最佳化工具嘗試一項適用於最大潛在資料列大小的計畫,可能會犧牲效能。當處理查詢時,中繼資料表和運算子可能需要儲存和處理比任何輸入資料列還寬的資料列。這些資料列的寬度,有時會使特定運算子無法處理資料列。如果發生這個情況,在查詢執行期間,Database Engine 會產生一則錯誤。您可以利用 ROBUST PLAN 來指示查詢最佳化工具,不考慮任何可能發生這個問題的查詢計畫。如果不可能執行這類計畫,查詢最佳化工具會傳回錯誤,而不是將錯誤偵測延遲到查詢執行時。資料列可能包含可變長度的資料行;Database Engine 允許資料列定義成超出 Database Engine 處理能力的最大潛在大小。一般而言,雖然有最大潛在大小,但應用程式仍會儲存實際大小在 Database Engine 處理能力限制之內的資料列。如果 Database Engine 發現太長的資料列,便會傳回執行錯誤。
KEEP PLAN
強制查詢最佳化工具放寬查詢的估計重新編譯臨界值。估計重新編譯臨界值是資料表因執行 UPDATE、DELETE、MERGE 或 INSERT 陳述式而變更了估計數目的索引資料行時,會自動重新編譯查詢的點。指定 KEEP PLAN 可確保查詢不會依照資料表有多項更新的頻率來重新編譯。KEEPFIXED PLAN
強制查詢最佳化工具不因統計資料中的變更而重新編譯查詢。指定 KEEPFIXED PLAN 可確保只有在基礎資料表的結構描述有了改變,或針對這些資料表執行了 sp_recompile 時,才重新編譯查詢。EXPAND VIEWS
指定展開索引檢視表,且查詢最佳化工具不會用任何索引檢視表來替代查詢的任何部分。當檢視表名稱被文字查詢中的檢視表定義取代時,便會展開這份檢視表。這個查詢提示會虛擬地禁止直接在查詢計畫中使用索引檢視表及其索引。
只有在查詢的 SELECT 部分直接參考索引檢視表,且已指定 WITH (NOEXPAND) 或 WITH (NOEXPAND, INDEX( index_value [ ,...n ] ) ) 時,才不展開這份索引檢視表。如需有關查詢提示 WITH (NOEXPAND) 的詳細資訊,請參閱<FROM>。
這個提示只會影響各陳述式 SELECT 部分中的檢視表,其中包括 INSERT、UPDATE、MERGE 和 DELETE 陳述式之 SELECT 部分中的檢視表。
MAXRECURSION number
指定這項查詢所能擁有的最大遞迴數目。number 是 0 和 32767 之間的非負整數。當指定 0 時,不會套用任何限制。如果未指定這個選項,伺服器的預設限制是 100。在查詢執行期間,當到達 MAXRECURSION 限制的指定或預設數目時,查詢會結束,且會傳回錯誤。
陳述式的所有效果都會因這個錯誤而回復。如果陳述式是 SELECT 陳述式,可能會傳回部分結果,或根本不傳回任何結果。任何傳回的部分結果都不會包括超出指定的最大遞迴層級之遞迴層級的所有資料列。
如需詳細資訊,請參閱<WITH common_table_expression (Transact-SQL)>。
USE PLAN N**'xml_plan'**
強制查詢最佳化工具將現有的查詢計畫用在 'xml_plan' 所指定的查詢上。如需詳細資訊,請參閱<以強制執行計畫來指定查詢計畫>。USE PLAN 無法搭配 INSERT、UPDATE、MERGE 或 DELETE 陳述式一起使用。TABLE HINT (exposed_object_name [ , <table_hint> [ [, ]...n] ] )
將指定的資料表提示套用至對應到 exposed_object_name 的資料表或檢視表。我們建議您只有在計畫指南的內容中,才將資料表提示當做查詢提示使用。exposed_object_name 可以是下列其中一個參考:
在查詢的 FROM 子句中,當某個別名用於資料表或檢視表時,exposed_object_name 就是別名。
沒有使用別名時,exposed_object_name 就是 FROM 子句中所參考之資料表或檢視表的完全相符項目。例如,如果資料表或檢視表使用兩部分名稱加以參考,exposed_object_name 就是相同的兩部分名稱。
當 exposed_object_name 已指定但沒有同時指定資料表提示時,在查詢中指定成物件之資料表提示一部分的任何索引都會被忽略,而且查詢最佳化工具會決定索引使用方式。當您無法修改原始的查詢時,就可以使用這項技巧來排除 INDEX 資料表提示的影響。請參閱範例 J。
<table_hint> ::= { [ NOEXPAND ] { INDEX ( index_value [ ,...n ] ) | INDEX = ( index_value ) | FASTFIRSTROW | FORCESEEK | HOLDLOCK | NOLOCK | NOWAIT | PAGLOCK | READCOMMITTED | READCOMMITTEDLOCK | READPAST | READUNCOMMITTED | REPEATABLEREAD | ROWLOCK | SERIALIZABLE | TABLOCK | TABLOCKX | UPDLOCK | XLOCK }
這是要套用至以查詢提示形式對應到 exposed_object_name 之資料表或檢視表的資料表提示。如需這些提示的描述,請參閱<資料表提示 (Transact-SQL)>。不允許使用 INDEX 和 FORCESEEK 以外的資料表提示當做查詢提示,除非查詢已經有一個指定資料表提示的 WITH 子句。如需詳細資訊,請參閱「備註」一節。
備註
除非是在陳述式內使用 SELECT 子句,否則,無法在 INSERT 陳述式中指定查詢提示。
您只能在最上層查詢中指定查詢提示,不能在子查詢中指定查詢提示。當資料表提示指定為查詢提示時,您可以在最上層查詢或子查詢中指定此提示。不過,在 TABLE HINT 子句中針對 exposed_object_name 所指定的值必須與查詢或子查詢中的公開名稱完全相符。
將資料表提示指定為查詢提示
我們建議您只有在計畫指南的內容中,才將 INDEX 或 FORCESEEK 資料表提示當做查詢提示使用。當您無法修改原始的查詢 (例如,因為它是協力廠商應用程式) 時,計畫指南就很有用。在計畫指南中指定的查詢提示會先加入至查詢,然後再進行編譯和最佳化。若為特定查詢,請在測試計畫指南陳述式時才使用 TABLE HINT 子句。如果是所有其他特定的查詢,我們建議您將這些提示指定成資料表提示。
當 INDEX 和 FORCESEEK 資料表提示指定為查詢提示時,對於下列物件是有效的:
資料表
檢視
索引檢視
通用資料表運算式 (此提示必須指定於結果集擴展此通用資料表運算式的 SELECT 陳述式內)。
動態管理檢視
具名子查詢
可以針對沒有任何現有資料表提示的查詢,將 INDEX 和 FORCESEEK 資料表提示指定為查詢提示,或是可以用這些提示來分別取代查詢中的現有 INDEX 或 FORCESEEK 提示。不允許使用 INDEX 和 FORCESEEK 以外的資料表提示當做查詢提示,除非查詢已經有一個指定資料表提示的 WITH 子句。在此情況下,也必須在 OPTION 子句中使用 TABLE HINT 將相符的提示指定為查詢提示,以保留此查詢的語意。例如,如果此查詢包含資料表提示 NOLOCK,則計畫指南的 @hints 參數中的 OPTION 子句也必須包含 NOLOCK 提示。請參閱範例 K。如果在 OPTION 子句中使用 TABLE HINT 來指定 INDEX 或 FORCESEEK 以外的資料表提示,但是沒有相符的查詢提示 (反之亦然),則會引發錯誤 8702,指出 OPTION 子句可能會造成查詢語意變更及導致查詢失敗。如需詳細資訊,請參閱<在計畫指南中使用 INDEX 和 FORCESEEK 查詢提示>。
範例
A. 使用 MERGE JOIN
下列範例指定由 MERGE JOIN 來執行查詢中的 JOIN 作業。
USE AdventureWorks;
GO
SELECT *
FROM Sales.Customer AS c
INNER JOIN Sales.CustomerAddress AS ca ON c.CustomerID = ca.CustomerID
WHERE TerritoryID = 5
OPTION (MERGE JOIN);
GO
B. 使用 OPTIMIZE FOR
下列範例會指示查詢最佳化工具在最佳化查詢時,將 'Seattle' 值用於區域變數 @city\_name 及使用統計資料判斷區域變數 @postal\_code 的值。
USE AdventureWorks;
GO
DECLARE @city_name nvarchar(30);
DECLARE @postal_code nvarchar(15);
SET @city_name = 'Ascheim';
SET @postal_code = 86171;
SELECT * FROM Person.Address
WHERE City = @city_name AND PostalCode = @postal_code
OPTION ( OPTIMIZE FOR (@city_name = 'Seattle', @postal_code UNKNOWN) );
GO
C. 使用 MAXRECURSION
您可以利用 MAXRECURSION 來防止形式不良的遞迴通用資料表運算式進入無限迴圈。下列範例會刻意建立無限迴圈,然後利用 MAXRECURSION 提示,將遞迴層級限制為 2。
USE AdventureWorks;
GO
--Creates an infinite loop
WITH cte (EmployeeID, ManagerID, Title) AS
(
SELECT EmployeeID, ManagerID, Title
FROM HumanResources.Employee
WHERE ManagerID IS NOT NULL
UNION ALL
SELECT cte.EmployeeID, cte.ManagerID, cte.Title
FROM cte
JOIN HumanResources.Employee AS e
ON cte.ManagerID = e.EmployeeID
)
--Uses MAXRECURSION to limit the recursive levels to 2
SELECT EmployeeID, ManagerID, Title
FROM cte
OPTION (MAXRECURSION 2);
GO
更正編碼錯誤之後,就不再需要 MAXRECURSION。
D. 使用 MERGE UNION
下列範例使用 MERGE UNION 查詢提示。
USE AdventureWorks;
GO
SELECT *
FROM HumanResources.Employee AS e1
UNION
SELECT *
FROM HumanResources.Employee AS e2
OPTION (MERGE UNION);
GO
E. 使用 HASH GROUP 和 FAST
下列範例使用 HASH GROUP 和 FAST 查詢提示。
USE AdventureWorks;
GO
SELECT ProductID, OrderQty, SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
WHERE UnitPrice < $5.00
GROUP BY ProductID, OrderQty
ORDER BY ProductID, OrderQty
OPTION (HASH GROUP, FAST 10);
GO
F. 使用 MAXDOP
下列範例使用 MAXDOP 查詢提示。
USE AdventureWorks ;
GO
SELECT ProductID, OrderQty, SUM(LineTotal) AS Total
FROM Sales.SalesOrderDetail
WHERE UnitPrice < $5.00
GROUP BY ProductID, OrderQty
ORDER BY ProductID, OrderQty
OPTION (MAXDOP 2);
GO
G. 使用 INDEX
下列範例使用 INDEX 提示。第一個範例會指定單一索引。第二個範例會針對單一資料表參考指定多個索引。在這兩個範例中,由於 INDEX 提示會套用在使用別名的資料表上,因此 TABLE HINT 子句也必須與公開物件名稱指定相同的別名。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide1',
@stmt = N'SELECT c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 2;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT(e, INDEX (IX_Employee_ManagerID)))';
GO
EXEC sp_create_plan_guide
@name = N'Guide2',
@stmt = N'SELECT c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 2;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT(e, INDEX(PK_Employee_EmployeeID, IX_Employee_ManagerID)))';
GO
H. 使用 FORCESEEK
下列範例使用 FORCESEEK 資料表提示。由於 INDEX 提示會套用在使用兩部分名稱的資料表上,因此 TABLE HINT 子句也必須與公開物件名稱指定相同的兩部分名稱。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide3',
@stmt = N'SELECT c.LastName, c.FirstName, HumanResources.Employee.Title
FROM HumanResources.Employee
JOIN Person.Contact AS c ON HumanResources.Employee.ContactID = c.ContactID
WHERE HumanResources.Employee.ManagerID = 3
ORDER BY c.LastName, c.FirstName;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT( HumanResources.Employee, FORCESEEK))';
GO
I. 使用多個資料表提示
下列範例會將 INDEX 提示套用到某個資料表,並將 FORCESEEK 提示套用到另一個資料表。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide4',
@stmt = N'SELECT e.ManagerID, c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 3;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT ( e, INDEX( IX_Employee_ManagerID ) )
, TABLE HINT ( c, FORCESEEK) )';
GO
J. 使用 TABLE HINT 來覆寫現有的資料表提示
下列範例會示範如何使用 TABLE HINT 提示,而不指定提示來覆寫查詢之 FROM 子句中所指定的 INDEX 資料表提示行為。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide5',
@stmt = N'SELECT e.ManagerID, c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e WITH (INDEX (IX_Employee_ManagerID))
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 3;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT(e))';
GO
K. 指定影響語意的資料表提示
下列範例在查詢中包含兩個資料表提示:影響語意的 NOLOCK 以及不會影響語意的 INDEX。為了保留查詢的語意,計畫指南的 OPTIONS 子句中會指定 NOLOCK 提示。除了 NOLOCK 提示以外,當編譯及最佳化陳述式時,也會指定 INDEX 和 FORCESEEK 提示,並用它們來取代查詢中不會影響語意的 INDEX 提示。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide6',
@stmt = N'SELECT c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
WITH (NOLOCK, INDEX (PK_Employee_EmployeeID))
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 3;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT ( e, INDEX( IX_Employee_ManagerID) , NOLOCK, FORCESEEK ))';
GO
下列範例示範另一個方法來保留查詢的語意,並讓最佳化工具選擇使用不是資料表提示中所指定的索引。其作法是在 OPTIONS 子句中指定 NOLOCK 提示 (因為它會影響語意),並在指定 TABLE HINT 關鍵字時,只包含資料表參考而沒有 INDEX 提示。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide7',
@stmt = N'SELECT c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
WITH (NOLOCK, INDEX (PK_Employee_EmployeeID))
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 2;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT ( e, NOLOCK))';
GO