2012-05-25 38 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 + ')') 
相关问题