有這樣一種方法。SQL Server 2005引進了計劃指南,它可以讓你把提示增加到特殊的查詢中而不用直接改變任何查詢本身。某些情況下,你不能更改源代碼(或由於源代碼是不能用的或源代碼是嚴禁使用的),那麼這可以是很有用的一種工具。然而,要注意這是一種高級的功能,所以使用時要多加注意。
你可以創建三種類型的計劃指南。下面來自SQL Server聯機幫助的摘錄總結了這幾種計劃指南:
OBJECT 計劃指南與事務-SQL存儲過程、純量函數、跨語句表-值函數和DML觸發器的上下文環境下執行的查詢相匹配。
SQL 計劃指南與單機事務-SQL語句和不是數據庫對象的成分上下文環境下執行的查詢相匹配。基於SQL的計劃指南也可以用來匹配確定指定表的參數的查詢。
TEMPLATE 計劃指南與確定指定表的參數的單機查詢相匹配。這些計劃指南用來取代一系列的查詢成為一個數據庫當前參數化的數據庫設置選項。
一個計劃指南的定義是通過系統存儲過程sp_create_plan_guide來實現的:
sp_create_plan_guide parameters
EXEC sp_create_plan_guide @name, @stmt, @type, @module_or_batch, @params, @hints
Here is an explanation of the parameters:
@name - name of the plan guide
@stmt - a T-SQL statement or batch
@type - indicates the type of guide (OBJECT, SQL, or TEMPLATE)
@module_or_batch - the name of a module (i.e. a stored procedure)
@params - for SQL and TEMPLATE guides, a string of all parameters for a T-SQL batch to be matched by this plan guide
@hints - OPTION clause hint to attach to a query as defined in the @stmt parameter
讓我們看看下面的例子,該例子模擬一個供應商應用程序執行一個內嵌在它們應用程序代碼中的准備好的查詢。下面參數化的動態SQL語句獲得一個ProductID的SalesOrderDetail信息。
USE AdventureWorks
GO
EXEC sp_executesql
@stmt = N'SELECT * FROM Sales.SalesOrderDetail WHERE ProductID = @ProductID',
@params = N'@ProductID int', @ProductID = 870
GO
EXEC sp_executesql
@stmt = N'SELECT * FROM Sales.SalesOrderDetail WHERE ProductID = @ProductID',
@params = N'@ProductID int', @ProductID = 897
GO
最初執行針對ProductID = 870的查詢產生一次表掃描。由於ProductID出現在總共有121,317行的第4,688行,優化器確定了這是一個最好的計劃。該計劃將緩存並且為後續該查詢的執行做好准備。
圖1
然而,盡管只有兩行存在於這個ProductID的Sales.SalesOrderDetail,針對ProductID = 897的第二次執行也還是使用了一次表掃描,在這個例子中,一個索引查找將是一個更好的選擇,但是現在我們依賴於這個執行計劃,該計劃產生於第一次執行准備查詢的時候。在一些情況下,這將成為一個棘手的性能問題,一個數據庫管理員將增加一個重新編譯的選項,促使優化器重新編譯該語句並且每次它執行時都得到優化。
創建SQL計劃指南解決了不能或不允許把提示怎發到數據庫查詢中的問題。下面的計劃指南創建並命名為GETSALESPRODUCTS_RECOMPILE_Fix,它把重新編譯應用到那個給我們帶來悲痛的SELECT語句。
USE AdventureWorks
GO
EXEC sp_create_plan_guide
@name = N'GETSALESPRODUCTS_RECOMPILE_Fix',
@stmt = N'SELECT * FROM Sales.SalesOrderDetail WHERE ProductID = @ProductID',
@type = N'SQL',
@module_or_batch = NULL,
@params = N'@ProductID int',
@hints = N'OPTION (RECOMPILE)'
GO
重新運行這些查詢,適當的計劃指南促使SQL語句重新編譯,產生最理想的查詢計劃,由此在ProductID中實現傳遞。這樣就避免了更改任何的代碼。通過觀察執行計劃,我們可以看到對一張表的掃描可以用於ProductID = 870,但是一個索引的查找可以用在ProductID = 897。一個文件性能分析(profiler trace)也顯示在該語句在執行之前被重新編譯了:
圖2
圖3
正如你所看到的,這是調整你之前不能訪問的查詢的有效方式。在SQL Server 2005之前,調整那些與這些查詢類似、隱藏在應用程序代碼深處、不能訪問的查詢是非常重要的。
你可以通過下面的查詢來查看在數據庫中存儲的所有計劃指南列表:
SELECT * FROM sys.plan_guides
GO
圖4
你可能一直在思考:如果我們確定自己不再需要它,那麼我們怎樣才能擺脫一個計劃指南呢?這種情況可以用另一個叫做sp_control_plan_guide的系統存儲過程來控制。你可以使用這個存儲過程來刪除計劃指南,把它停用,或者如果之前已經停止它,那麼重新啟用它。
sp_control_plan_guide parameters
EXEC sp_control_plan_guide @Operation, @name
Explanation of its parameters:
@Operation - a control option; one of DROP, DROP ALL, DISABLE, DISABLE ALL, ENABLE, ENABLE ALL
@name - name of the plan guide to control
如果需要,接下來我們可以執行下面的語句來刪除這個計劃指南。
EXEC sp_control_plan_guide N'DROP', N'GETSALESPRODUCTS_RECOMPILE_Fix'
GO
由於計劃指南影響了優化器的執行計劃選擇,所以當訪問數據庫查詢受到限制時,在特殊情況下必須認真使它們得到平衡(和記錄!)。