SQL Server存储过程中如何在WHERE条件中传递数组参数
解决SQL Server存储过程处理逗号分隔字符串IN查询的问题
我来帮你搞定这个问题——你当前的存储过程没法正确解析逗号分隔的字符串,因为IN (@RigNameValue)会把整个"ABC,DEF"当成单个匹配值,而不是拆分成"ABC"和"DEF"两个独立的条件。下面给你几种实用的解决方案,按推荐程度排序:
方法1:使用内置的STRING_SPLIT函数(推荐,SQL Server 2016+)
SQL Server 2016及以上版本提供了内置的字符串拆分函数STRING_SPLIT,可以直接把逗号分隔的字符串拆成一行行的结果集,完美适配IN查询。修改后的存储过程如下:
ALTER PROCEDURE [dbo].[BookedChemicalsLists] @RigNameValue VARCHAR(50) = null AS BEGIN SET NOCOUNT ON; SELECT * FROM [BookedChemicals] WHERE -- 当参数为null时返回所有数据,否则匹配拆分后的RigName (@RigNameValue IS NULL OR RigName IN (SELECT value FROM STRING_SPLIT(@RigNameValue, ','))) END
说明:STRING_SPLIT会将输入的字符串按指定分隔符(这里是逗号)拆分,返回一个包含value列的表,IN子查询会遍历这个表的所有值来匹配RigName。
方法2:自定义字符串拆分函数(兼容SQL Server 2016之前版本)
如果你用的是更早的SQL Server版本,没有STRING_SPLIT,可以自己写一个表值函数来拆分字符串:
首先创建拆分函数:
CREATE FUNCTION dbo.SplitString ( @InputString NVARCHAR(MAX), -- 要拆分的字符串 @Delimiter NVARCHAR(5) -- 分隔符 ) RETURNS @OutputTable TABLE (Value NVARCHAR(MAX)) AS BEGIN DECLARE @StartIndex INT, @EndIndex INT SET @StartIndex = 1 -- 确保字符串末尾有分隔符,避免最后一个值被遗漏 IF SUBSTRING(@InputString, LEN(@InputString), 1) <> @Delimiter BEGIN SET @InputString = @InputString + @Delimiter END WHILE CHARINDEX(@Delimiter, @InputString) > 0 BEGIN SET @EndIndex = CHARINDEX(@Delimiter, @InputString) -- 截取当前分隔符前的子串并插入结果表 INSERT INTO @OutputTable(Value) SELECT SUBSTRING(@InputString, @StartIndex, @EndIndex - @StartIndex) -- 截取剩余字符串继续循环 SET @InputString = SUBSTRING(@InputString, @EndIndex + 1, LEN(@InputString)) END RETURN END
然后修改存储过程调用这个函数:
ALTER PROCEDURE [dbo].[BookedChemicalsLists] @RigNameValue VARCHAR(50) = null AS BEGIN SET NOCOUNT ON; SELECT * FROM [BookedChemicals] WHERE (@RigNameValue IS NULL OR RigName IN (SELECT Value FROM dbo.SplitString(@RigNameValue, ','))) END
方法3:动态SQL(不推荐,需注意SQL注入)
如果以上两种方法都不适用,也可以用动态SQL拼接查询语句,但必须做好参数化来避免SQL注入风险:
安全的参数化动态SQL版本
ALTER PROCEDURE [dbo].[BookedChemicalsLists] @RigNameValue VARCHAR(50) = null AS BEGIN SET NOCOUNT ON; DECLARE @SQL NVARCHAR(MAX) DECLARE @ParamDefinition NVARCHAR(MAX) = '@RigNames VARCHAR(50)' -- 基础查询语句 SET @SQL = 'SELECT * FROM [BookedChemicals] WHERE 1=1 ' -- 如果参数不为空,拼接IN条件(用STRING_SPLIT保证安全) IF @RigNameValue IS NOT NULL BEGIN SET @SQL = @SQL + 'AND RigName IN (SELECT value FROM STRING_SPLIT(@RigNames, '',''))' END -- 执行参数化的动态SQL EXEC sp_executesql @SQL, @ParamDefinition, @RigNames = @RigNameValue END
非参数化版本(不推荐,有注入风险)
如果必须用字符串拼接的方式,一定要对输入做转义,但还是不建议:
ALTER PROCEDURE [dbo].[BookedChemicalsLists] @RigNameValue VARCHAR(50) = null AS BEGIN SET NOCOUNT ON; DECLARE @SQL NVARCHAR(MAX) SET @SQL = 'SELECT * FROM [BookedChemicals] WHERE 1=1 ' IF @RigNameValue IS NOT NULL BEGIN -- 把逗号替换为'','',拼接成('ABC','DEF')的格式 SET @SQL = @SQL + 'AND RigName IN (''' + REPLACE(@RigNameValue, ',', ''',''') + ''')' END EXEC sp_executesql @SQL END
总结
- 优先选择
STRING_SPLIT,它是官方内置函数,性能和安全性都有保障; - 旧版本SQL Server用自定义拆分函数;
- 动态SQL尽量用参数化版本,避免直接拼接用户输入,防止SQL注入。
内容的提问来源于stack exchange,提问作者Nabeel Mahmood
相关产品推荐
相关产品推荐

