2010-09-09 66 views
4

我們有一個存儲過程,它構建一些動態SQL並通過參數化調用sp_executesql執行。爲什麼存儲過程在遠程執行到本地時會執行不同的操作?

在正常情況下,這個功能非常出色,並且在程序的執行時間(~8秒〜1秒)中獲得了很大的好處,但是在某些未知的情況下,出現了一些奇怪的事情,其他方式(〜31秒),但只有在通過RPC執行時(即從.Net應用程序調用SqlCommand.CommandTypeCommandType.StoredProcedure;或作爲來自鏈接服務器的遠程查詢) - 如果使用SQL Server Management作爲SQL批處理執行工作室,我們看不到性能下降。

改變生成的SQL中的空白並重新編譯存儲過程似乎至少在短期內解決了這個問題,但我們想要了解原因或強制執行計劃的方法爲生成的SQL重新生成;但目前,我不知道如何繼續進行?


爲了說明,存儲過程,看起來有點像:

CREATE PROCEDURE [dbo].[usp_MyObject_Search] 
    @IsActive  AS BIT = NULL, 
    @IsTemplate  AS BIT = NULL 
AS 
DECLARE @WhereClause NVARCHAR(MAX) = '' 

IF @IsActive IS NOT NULL 
BEGIN 
    SET @WhereClause += ' AND (svc.IsActive = @xIsActive) ' 
END 

IF @IsTemplate IS NOT NULL 
BEGIN 
    SET @WhereClause += ' AND (svc.IsTemplate = @xIsTemplate) ' 
END 

DECLARE @Sql NVARCHAR(MAX) = ' 
    SELECT  svc.[MyObjectId], 
       svc.[Name], 
       svc.[IsActive], 
       svc.[IsTemplate] 

    FROM  dbo.MyObject svc WITH (NOLOCK) 

    WHERE 1=1 ' + @WhereClause + ' 

    ORDER BY svc.[Name] Asc' 

EXEC sp_executesql @Sql, N'@xIsActive BIT, @xIsTemplate BIT', 
        @xIsActive = @IsActive, @xIsTemplate = @IsTemplate 

通過這種方法,查詢計劃將被緩存爲NULL /不爲NULL的排列,和我們獲得緩存查詢計劃的好處。我不明白的是,爲什麼在「發生某些事情」之後,在遠程執行與本地執行時使用不同的查詢計劃;我也不明白「什麼」可能是什麼?

我意識到我可以移動從參數化了,但那麼我們就會失去緩存是什麼通常良好的執行計劃的好處。

回答

3

我會懷疑參數嗅探。如果你是SQL Server 2008上,你可以嘗試,包括OPTIMIZE FOR UNKNOWN減少,當它產生一個計劃,它這樣做非典型參數值的機會。

RE:What I don't understand is why it would use a different query plan when executed remotely vs. locally after "something happens"

當您在SSMS執行它不會使用,因爲不同SET選擇同一不錯的計劃(如SET ARITHABORT ON),所以它會編譯爲參數值你是行之有效一個新的計劃正在測試。

你可以看到這些計劃與

SELECT usecounts, cacheobjtype, objtype, text, query_plan, value as set_options 
FROM sys.dm_exec_cached_plans 
CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) 
cross APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa 
where text like '%FROM  dbo.MyObject svc WITH (NOLOCK)%' 
              and attribute='set_options' 

編輯

下位只是響應badbod99的回答

create proC#foo @mode bit, @date datetime 
as 
declare @Sql nvarchar(max) 

if(@mode=1) 
set @Sql = 'select top 0 * from sys.objects where create_date < @date /*44FC79BD-2AF5-4774-9674-04D6C3D4B228*/' 
else 
set @Sql = 'select top 0 * from sys.objects where modify_date < @date /*44FC79BD-2AF5-4774-9674-04D6C3D4B228*/' 

EXEC sp_executesql @Sql, N'@date datetime', 
        @date = @date 
go 

declare @d datetime 
set @d = getdate() 
exeC#foo 0,@d 
exeC#foo 1, @d 

SELECT usecounts, cacheobjtype, objtype, text, query_plan, value as set_options 
FROM sys.dm_exec_cached_plans 
CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) 
cross APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa 
where text like '%44FC79BD-2AF5-4774-9674-04D6C3D4B228%' 
              and attribute='set_options' 

返回

enter image description here

+0

您是否顯示2個計劃已準備好?或者它使用相同的計劃? – badbod99 2010-09-09 11:41:49

+0

@ badbod99它將爲傳遞給'EXEC sp_executesql'的不同字符串創建一個不同的Prepared Compiled Plan,並且這與父存儲過程的編譯無關。無論如何,這必須是這種情況,因爲這些字符串可能涉及完全不同的對象。 – 2010-09-09 12:07:30

+0

好點,execute_sql本身就是一個SP(它看起來像是帶有RECOMPILE查詢提示集),因此根據傳入的數據編譯它自己的執行計劃。所以理論上,使用這個問題不應該發生。仍值得重新編譯。 – badbod99 2010-09-09 12:48:57

1

重新編譯

任何時間SP的執行將顯著不同,由於條件語句,將其從最後一個請求緩存執行計劃可能不是最佳的這一個。

這是關於何時SQL編譯SP的執行計劃的全部內容。他們關於Microsoft docs上sp編譯的關鍵部分是這樣的:

...這個優化在SQL Server重新啓動後第一次運行存儲過程時自動發生。如果存儲過程使用的基礎表發生更改,也會發生這種情況。但是,如果添加一個新的索引,存儲過程可能從中受益,則直到下次重新啓動SQL Server後運行存儲過程時纔會進行優化。在這種情況下,它可以迫使存儲過程,它執行

SQL並重新編譯有時執行計劃的下一次重新編譯有用的,從Microsoft docs

SQL Server自動重新編譯存儲過程和當它有利於觸發時觸發。

...但它不會在每次調用(除非使用WITH RECOMPILE),因此,如果每次執行可能會導致不同的SQL,你可以用相同的舊計劃停留至少一個來完成這一操作。

RECOMPILE查詢提示

The RECOMPILE query hint,檢查需要在語句級重新編譯什麼的時候考慮到您的參數值。

WITH重新編譯選項

WITH RECOMPILE(見F)將導致執行計劃每次調用編譯,所以你永遠不會有一個次優方案,但你必須編譯開銷。

重組爲多個SP

看你的具體情況,爲PROC執行計劃不會改變和2條SQL語句應該已經準備執行計劃。

我會建議重組代碼分裂的SP,而不是有這樣的條件,SQL生成會簡化事情,並確保你總是沒有任何SQL魔力醬最優的執行計劃。

+0

生成的SQL沒有任何'在它或'語句(其實我們改變了它被parametrised動態SQL *刪除*之類的事情) – 2010-09-09 09:53:53

+0

這不只是與生成的SQL做的,這是做每次生成的SQL都不相同。查看我的編輯。 – badbod99 2010-09-09 09:57:30

+0

@ badhod99就不會產生不同的SQL每一次 - 它會使用相同的SQL爲NULL /非NULL的相同組合 - 我加的是什麼,我們已經在此刻得到了(略簡體)版本這個問題。 – 2010-09-09 10:14:33

相關問題