下面是示例查询。
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
为空,查询应返回该条件的所有行。
请指导我如何做到这一点?还建议如何编写此查询以优化性能。
您都需要动态SQL或拆分函数,因为IN ('1,2,3')
与IN (1,2,3)
不同。
拆分功能:
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 或更高版本时,您应该查看表值参数(此处的示例)。
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 + ')')