2012-05-25 39 views
2

以下是樣本查詢。如果IN子句沒有值,如何返回所有行?

CREATE PROCEDURE GetModel 
(
    @brandids varchar(100), -- brandid="1,2,3" 
    @bodystyleid varchar(100) -- bodystyleid="1,2,3" 

) 
AS 
    select * from model 
    where brandid in (@brandids) -- use a UDF to return table for comma delimited string 
    and bodystyleid in (@bodystyleid) 

我的要求是,如果@brandids@bodystyleid是空白,查詢應返回的所有行的這一條件。

請指導我如何做到這一點?還建議如何編寫此查詢以優化性能。

+0

可以顯示調用存儲過程的代碼嗎?對於傳遞列表有一些技巧,你可以利用它從.NET語言中調用。 – JamieSee

+0

嗨jamieSee,我正在使用實體框架4.0,所以我打電話給它在EF中調用SP的方式 – Paul

回答

5

因爲IN ('1,2,3')IN (1,2,3)不同,所以您需要動態SQL或拆分功能。

拆分功能:

CREATE FUNCTION dbo.SplitInts 
(
    @List  VARCHAR(MAX), 
    @Delimiter CHAR(1) 
) 
RETURNS TABLE 
AS 
    RETURN (SELECT Item = CONVERT(INT, Item) FROM ( 
    SELECT Item = x.i.value('(./text())[1]', 'int') FROM ( 
     SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(@List, @Delimiter, '</i><i>') 
     + '</i>').query('.')) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y 
    WHERE Item IS NOT NULL 
    ); 

代碼變成類似:

SELECT m.col1, m.col2 FROM dbo.model AS m 
LEFT OUTER JOIN dbo.SplitInts(NULLIF(@brandids, ''), ',') AS br 
ON m.brandid = COALESCE(br.Item, m.brandid) 
LEFT OUTER JOIN dbo.SplitInts(NULLIF(@bodystyleid, ''), ',') AS bs 
ON m.bodystyleid = COALESCE(bs.Item, m.bodystyleid) 
WHERE (NULLIF(@brandids, '') IS NULL OR br.Item IS NOT NULL) 
AND (NULLIF(@bodystyleid, '') IS NULL OR bs.Item IS NOT NULL); 

(請注意,我加了很多NULLIF的處理在這裏......如果這些參數不具有價值,您應該傳遞NULL,而不是「空白」。)

動態SQL將導致由於參數嗅探導致計劃錯誤的機會少得多:

DECLARE @sql NVARCHAR(MAX); 

SET @sql = N'SELECT columns FROM dbo.model 
WHERE 1 = 1 ' 
+ COALESCE(' AND brandid IN (' + @brandids + ')', '') 
+ COALESCE(' AND bodystyleid IN (' + @bodystyleid + ')', ''); 

EXEC sp_executesql @sql; 

當然作爲@JamieCee中所指出的,動態的SQL 可能易受注射,如果搜索動態SQL的任何地方,你會發現。所以如果你不相信你的輸入,你會想要防範潛在的注入攻擊。就像您在應用程序代碼中組裝臨時SQL一樣。

當您遷移到SQL Server 2008或更高版本時,您應該查看table-valued parametersexample here)。

+0

不要做這裏顯示的動態SQL,它是注入漏洞。與其他建議一起去,並使用將值加載到臨時表或表變量中的拆分函數。 – JamieSee

+1

@JamieSee如果用戶實際上在表單或文章的某個文本字段中鍵入「1,2,3」,那麼只有注入漏洞 - 如果此輸入來自更可信的源,則沒有理由不要使用錫鋁箔帽尚未。此外,爲什麼臨時表或表變量是一個需求?大量的分割解決方案不需要將結果放入臨時對象中。 –

+0

確實如此,但我們也不知道這不是直接從用戶輸入或從用戶輸入中組合。面對未知的使用情況,我寧願在謹慎的方面犯錯。如果在用戶和存儲過程之間的某處沿着非字符串值類型強制執行,那麼我同意你所提議的是可以的。然而,這也意味着,當它在其他地方被重用時,你必須確保新調用者的行爲是相同的 - 我以前見過的開發團隊失敗了。 – JamieSee

0
if(@brandids = '' or @brandids is null) 
Begin 
    Set @brandids = 'brandid' 
End 

if(@bodystyleid = '' or @bodystyleid is null) 
Begin 
    Set @bodystyleid = 'bodystyleid' 
End 

Exec('select * from model where brandid in (' + @brandids + ') 
and bodystyleid in (' + @bodystyleid + ')') 
相關問題