2008-10-23 27 views
3

我正在使用多個可選參數的存儲過程。其中一些參數是單值,它是很容易使用WHERE子句,如:使用可選的存儲過程參數

WHERE (@parameter IS NULL OR column = @parameter) 

然而,在某些情況下,WHERE條件比較複雜:

WHERE (@NewGroupId IS NULL OR si.SiteId IN (SELECT gs.SiteId 
FROM [UtilityWeb].[dbo].[GroupSites] AS gs 
WHERE gs.GroupId = @NewGroupId)) 

當我取消這些複雜在WHERE子句中,查詢執行時間加倍並且執行計劃變得非常複雜。雖然執行計劃不會影響我,但將查詢的執行時間加倍是一個明確的問題。

是否有其他人發現了,在他們的存儲過程的可選參數工作最佳實踐或模式?

這是動態SQL會是更好的解決方案嗎?

+0

執行計劃應該會打擾你,因爲它告訴你到底查詢執行器究竟在做什麼。 – 2008-10-23 13:15:59

+0

複雜性並沒有直接打擾我,因爲我有一些非常複雜的查詢可以快速運行。可悲的是,這並不是。 – 2008-10-23 13:47:03

+0

另請參閱:http://stackoverflow.com/questions/532468/ignoring-a-null-parameter-in-t-sql/532510#532510 – 2009-02-12 15:06:43

回答

4

的主要問題可能是parameter sniffing,以及很大的不同,這取決於你的參數是NULL最優的執行計劃。嘗試使用RECOMPILE運行存儲的proc。

與一些信念相反,Sql Server doesdo短路評估 - 儘管(與所有查詢優化一樣),它可能並不是您想要的。

順便說一句 - 我可能會重寫查詢,作爲加入派生表的那部分:

SELECT * 
FROM Table as si 
JOIN (
    SELECT SiteId 
    FROM [UtilityWeb].[dbo].[GroupSites] 
    WHERE GroupId = ISNULL(@NewGroupId, GroupId) 
    /* --Or, if all SiteIds aren't in GroupSites, or GroupSites is unusually large 
    --this might work better 
    SELECT @newGroupId 
    UNION ALL 
    SELECT SiteId FROM [UtilityWeb].[dbo].[GroupSites] 
    WHERE GroupId = @NewGroupId 
    */ 
) as gs ON 
    si.SiteId = gs.SiteId 

它可能會或可能不會影響查詢計劃,但它是一個有點清潔劑給我。

5

我會爲參數是否可用創建單獨的查詢。

這將創建更簡單的SQL,優化器將會做得更好。

像這樣:

if (@parameter IS NULL) then begin 
    select * from foo 
end 
else begin 
    select * from foo where value = @parameter 
end 

在你有許多參數重新設計這樣的,你去的動態SQL解決方案,然後也一直使用的參數,你可能會被錯誤SQL-Injection得到咬傷。

組合也是可能的。您最有可能使用的查詢/查詢完全編碼,並進行預編譯。所有其他組合都是動態創建的。

+0

這可能是最好的方法,是的,而不是動態SQL或使用ISNULL函數,因爲ISNULL不會將零長度字符串視爲null。這取決於你的程序是如何被調用的(從什麼應用程序等),這是一個特別的問題。此外,執行可能會更快,使用if結構。 只需注意:上面的if結構也應該測試數據長度(@參數)> 0,以避免零長度的字符串問題。 – 2010-09-01 09:42:44

2

動態SQL可能是在這種情況下,一個更好的解決方案,特別是如果存儲過程只包裝這個查詢。

有一點要記住的是,SQL Server不會做布爾表達式的短路單個查詢中。在許多語言中,「(a)||(b)」不會導致b被評估,如果a是真的。同樣,「(a)& &(b)」如果a爲假,則不會導致b被評估。在SQL Server中,情況並非如此。因此,在您給出的示例中,即使@NewGroupId不爲空,「or」後端上的查詢也會得到評估。

+0

Sql Server確實做短路評估。 – 2008-10-23 13:57:10

+0

我很確定它在過去的某個時間點沒有 - 至少對於「if」語句 - 但我承認我沒有測試過,因爲可能是SQL Server 7. – 2008-10-23 15:23:47

2

對於少數可選參數,有條件的選擇從一個幾個靜態查詢作爲GVS提示的是OK。

然而,這變得很困難,如果有一個幾個參數,因爲你需要處理所有排列 - 5個參數是32個靜態查詢!使用動態SQL,您可以構建最符合給定參數的精確查詢。一定要使用綁定變量!

4

CASE語句是你的朋友?

不是:

if (@parameter IS NULL) then begin 
    select * from foo 
end 
else begin 
    select * from foo where value = @parameter 
end 

您可以使用:

SELECT * FROM foo 
WHERE value = CASE WHEN @parameter IS NULL THEN value ELSE @parameter END 

或者

SELECT * FROM foo 
WHERE value = ISNULL(@parameter,value) 

我傾向於使用CASE陳述更多,因爲我的選擇l參數可能使用某些值而不是NULL ...

1

恕我直言,參數嗅探問題可以通過將所有參數複製到變量中解決;那麼請避免直接使用參數,而應該使用變量。例如:


create proc ManyParams 
(
    @pcol1 int, 
    @pcol2 int, 
    @pcol3 int 
) 
as 
declare 
    @col1 int, 
    @col2 int, 
    @col3 int 

select 
    @col1 = @pcol1, 
    @col2 = @pcol2, 
    @col3 = @pcol3 

select 
    col1, 
    col2, 
    col3 
from 
    tbl 
where 
    1 = case when @col1 is null then 1 else case when col1 = @col1 then 1 else 0 end end 
and 1 = case when @col2 is null then 1 else case when col2 = @col2 then 1 else 0 end end 
and 1 = case when @col3 is null then 1 else case when col3 = @col3 then 1 else 0 end end