sp_executesql 更安全因其支持参数化防sql注入;exec() 拼接字符串易受注入攻击,外部输入须全转为参数,动态对象名需白名单或quotename()处理,参数声明须用nvarchar(max),执行计划缓存依赖sql字符串完全一致。

sp_executesql 为什么比 EXEC() 更安全
因为 sp_executesql 支持参数化,能天然防 SQL 注入;而拼接字符串后用 EXEC() 执行,一旦变量没过滤干净,username = 'admin' OR 1=1 --' 这种输入就直接穿透进查询里。
常见错误现象:用 EXEC(@sql) 处理用户传入的排序字段或搜索关键词,本地测试没问题,上线后被扫出注入漏洞。
- 必须把所有外部输入(尤其是
@search_term、@order_by)转成sp_executesql的参数,而不是拼进字符串 - 动态列名、表名、排序字段不能参数化——它们得走白名单校验或
QUOTENAME()包裹,比如QUOTENAME(@sort_column) -
sp_executesql的参数声明必须是NVARCHAR类型,且长度足够(推荐NVARCHAR(MAX)),否则会截断导致语法错误
动态 WHERE 条件怎么写才不崩
最常踩的坑是空条件拼出 WHERE AND ... 或漏掉 1=1 导致语法错误。别手动拼 AND,用逻辑组合更稳。
使用场景:后台筛选接口,多个可选字段(姓名、部门、状态、日期范围)。
- 初始化
@sql为N'SELECT * FROM users WHERE 1=1',后续每个条件都以AND ...追加 - 对每个可选参数,先判断是否非空/有效,再追加对应子句,例如:
IF @name IS NOT NULL SET @sql += N' AND name LIKE @name_param' - 日期范围要小心
NULL:用IS NULL判断边界值,别直接拼BETWEEN NULL AND ...,SQL Server 会跳过整个条件
输出参数和返回结果集能一起用吗
可以,但要注意顺序和声明方式:sp_executesql 的参数列表里,输出参数必须显式标出 OUTPUT,而且得在调用时也带 OUTPUT 关键字,否则值不会回传。
性能影响:如果只想要计数或单值,优先用输出参数;如果要查数据,就靠结果集——别为了省一次执行,硬把 COUNT 和主查询塞进同一个 sp_executesql 里,可读性和维护性会陡降。
- 输出参数声明格式:
N'@total INT OUTPUT', @total OUTPUT,前后两个OUTPUT缺一不可 - 不能在同一个
sp_executesql调用中既返回结果集又用输出参数接收聚合值(比如SELECT @cnt = COUNT(*)+SELECT *),SQL Server 不允许混合模式 - 如果真需要总数+分页数据,老实用两次:一次
COUNT(*)输出参数,一次主查询
SQL Server 版本兼容性和执行计划缓存
sp_executesql 的执行计划能复用,但前提是每次生成的 @sql 字符串完全一致(包括空格、换行、大小写)。很多人忽略这点,以为“用了参数就自动缓存”,结果发现每次执行都编译新计划,CPU 拉高。
容易被忽略的地方:动态拼接时混用 + 和 +=,中间多一个空格、少一个换行,或者 @status 值为 NULL 时分支逻辑不同,都会让哈希值变化。
- 用
PRINT @sql把最终语句打出来,人工比对几次不同参数下的输出,确认结构是否一致 - 避免在 SQL 字符串里拼入变量值(如
SET @sql = '... status = ' + CAST(@status AS VARCHAR)),这等于放弃参数化优势 - SQL Server 2005+ 都支持
sp_executesql,但低版本不支持NVARCHAR(MAX),若用NTEXT会报错,务必统一用NVARCHAR










