用参数化查询搞定MSSQL里in条件,操作更简单点吧
- 问答
- 2026-01-09 03:46:45
- 7
想当年,我刚接触MSSQL那会儿,最头疼的事情之一就是动态构建IN条件,前端传过来一个用户选择的ID列表,可能是三个,也可能是三百个,我怎么把它塞到SQL语句里?最开始用的笨办法是字符串拼接,把用户输入的ID用逗号连起来,然后直接塞进SQL字符串里,就像这样:
DECLARE @ids VARCHAR(MAX) = '1,2,3,5,8,13';
DECLARE @sql VARCHAR(MAX) = 'SELECT * FROM Users WHERE UserID IN (' + @ids + ')';
EXEC(@sql);
这么干,当时觉得挺聪明,省事了,但后来才知道,这简直是打开了“SQL注入攻击”的大门,相当于把自家大门的钥匙插在锁上还贴个纸条说“欢迎光临”,任何一个有点坏心眼的用户,如果不做严格检查,他传过来的可能不是1,2,3,而是1); DROP TABLE Users; --,那乐子可就大了,这种方法绝对要不得。
后来学乖了,知道了参数化查询是防注入的黄金法则,就是用@Parameter这种形式来代替直接拼接值,于是我又尝试了:
DECLARE @ids VARCHAR(MAX) = '1,2,3,5,8,13'; SELECT * FROM Users WHERE UserID IN (@ids);
结果你猜怎么着?报错了,或者更糟,它只返回了UserID = 1的数据,因为数据库把'1,2,3,5,8,13'这个整个字符串当成了一个值,它去找有没有用户的ID等于这个长长的字符串,当然找不到,这说明IN语句后面跟一个逗号分隔的字符串参数是行不通的,它不认识。
那怎么办呢?难道又要退回危险的字符串拼接吗?当然不,这时候就得请出“参数化查询”的进阶玩法了,核心思路就一句话:既然一个参数不行,我们就用很多个参数。
具体怎么搞?我参考了一些数据库专家的做法(比如像Stack Overflow上那些高赞回答里常提到的),总结出一个既安全又相对简单的套路,这个套路不需要你去搞懂什么复杂的专业化术语,表值参数”之类的,咱们就用最基本的东西来搞定。
第一步,把逗号分隔的字符串拆开。
在MSSQL里,有个挺方便的函数叫STRING_SPLIT(如果你是SQL Server 2016或更高版本),它干的就是这个活儿:把一个字符串按照指定的分隔符(比如逗号)拆成多行。

SELECT value FROM STRING_SPLIT('1,2,3,5,8,13', ',');
执行这句,你会得到一个单列的结果集,每一行就是一个数字:1, 2, 3, 5, 8, 13,这下,我们就有办法了。
第二步,把拆开的结果和我们的主表关联起来。
我们不直接用在IN里,而是把它当作一个“临时表”,用INNER JOIN或者EXISTS来关联查询。
假设我们是从应用程序(比如C#)传参过来的,完整的写法应该是这样:
在你的C#代码里,你还是正常传一个逗号分隔的字符串进来,string userIDs = "1,2,3,5,8,13";。

你的SQL查询语句写成参数化形式:
DECLARE @IdList NVARCHAR(MAX); SET @IdList = @PassedInIds; -- 这个@PassedInIds就是从C#传过来的参数 SELECT u.* FROM Users u INNER JOIN STRING_SPLIT(@IdList, ',') ids ON u.UserID = ids.value;
看明白了吗?我们不再把参数用在IN里面,而是让STRING_SPLIT函数把参数字符串“炸开”成多行数据,然后通过INNER JOIN连接Users表,只选择那些ID能匹配上的用户。
这种方法的好处简直太多了:
- 绝对安全: 整个过程都是参数化的,
@PassedInIds参数的值会被数据库引擎当作一个整体数据处理,不会被解释成SQL指令,彻底杜绝了注入攻击。 - 逻辑清晰: 代码一目了然,就是两个表(用户表和拆分出来的ID表)做关联查询,比动态拼接SQL字符串那种“巫术”好理解多了。
- 性能不错: 数据库优化器对这种连接查询有很好的支持,通常比一些复杂的动态SQL执行效率更高。
这里有个前提是你的SQL Server版本得是2016及以上,如果你的环境比较老,没有STRING_SPLIT函数,那也有办法,可以自己写一个简单的拆分函数,或者用XML方法来拆,原理都是一样的:想方设法把单个参数字符串变成一组可连接的行数据。
还有一种更“原生”的参数化方法,就是在程序端(比如C#)直接传递一个DataTable进去,在SQL Server端定义一个对应的表类型(Table-Valued Parameter),这种方法更“正宗”,性能也最好,尤其是当ID列表非常长的时候,但说实话,它需要你在数据库端额外创建类型,代码写起来也稍微啰嗦一点,对于大多数日常场景,上面那种用STRING_SPLIT的方法已经足够简单和实用了。
以后再在MSSQL里遇到动态IN条件的问题,别犹豫,忘掉字符串拼接,也别纠结于一个参数怎么塞进IN里,直接换个思路,用STRING_SPLIT把参数拆开,然后大大方方地JOIN一下,事情就变得简单又安全了,这个方法是我从多年的实际踩坑和经验分享中学来的,亲测有效,希望能帮你省下不少折腾的时间。
本文由太叔访天于2026-01-09发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/77207.html
