动态SQL存储过程可以根据运行时传入的参数动态调整查询逻辑,适配多变的业务查询需求,在SQL Server中,使用sp_executesql执行动态SQL是更安全高效的选择,相比EXEC直接执行拼接字符串,它支持参数化,还能复用查询执行计划。

动态SQL存储过程的应用场景
当业务查询的条件不固定时,比如用户可以选择按姓名、年龄、部门等多个字段组合查询,固定条件的存储过程需要写大量IF判断,代码冗余且维护成本高,此时动态SQL就能发挥作用。常见的适用场景包括:
- 多条件可选查询,用户可以选择部分条件进行筛选
- 查询的表名、字段名需要根据参数动态指定
- 排序规则、分页参数需要运行时动态调整
sp_executesql的基本用法
sp_executesql是SQL Server提供的系统存储过程,用于执行动态拼接的SQL语句,它的语法结构如下:
-- 基本语法
sp_executesql
@stmt = N'动态SQL语句', -- 要执行的SQL字符串,需要加N前缀表示Unicode
@params = N'参数定义', -- 可选,定义动态SQL中用到的参数
@参数名1 = 参数值1, -- 给动态SQL中的参数赋值
@参数名2 = 参数值2
需要注意,动态SQL语句和参数定义都需要用N前缀标记为Unicode字符串,否则可能出现中文乱码或者执行报错的问题。
编写动态SQL存储过程的完整示例
下面以员工表查询为例,编写一个支持可选条件查询的动态存储过程,员工表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 员工ID,主键 |
| name | nvarchar(50) | 员工姓名 |
| age | int | 员工年龄 |
| department | nvarchar(50) | 所属部门 |
存储过程实现代码
这个存储过程支持传入姓名、年龄、部门三个可选参数,传入对应参数就按该条件筛选,不传入则忽略该条件:
CREATE PROCEDURE GetEmployeeByCondition
@name nvarchar(50) = NULL, -- 姓名筛选条件,默认NULL
@age int = NULL, -- 年龄筛选条件,默认NULL
@department nvarchar(50) = NULL -- 部门筛选条件,默认NULL
AS
BEGIN
SET NOCOUNT ON; -- 不返回受影响的行数,提升性能
-- 定义动态SQL基础语句
DECLARE @sql nvarchar(1000) = N'SELECT id, name, age, department FROM Employee WHERE 1=1';
-- 定义参数列表,对应动态SQL中使用的参数
DECLARE @param_def nvarchar(500) = N'@name nvarchar(50), @age int, @department nvarchar(50)';
-- 根据传入参数动态拼接条件
IF @name IS NOT NULL
SET @sql = @sql + N' AND name LIKE @name';
IF @age IS NOT NULL
SET @sql = @sql + N' AND age = @age';
IF @department IS NOT NULL
SET @sql = @sql + N' AND department = @department';
-- 执行动态SQL,传入参数
EXEC sp_executesql
@stmt = @sql,
@params = @param_def,
@name = @name,
@age = @age,
@department = @department;
END
GO
存储过程调用示例
调用上述存储过程的不同场景示例:
-- 只按姓名查询,查询姓名包含张三的员工 EXEC GetEmployeeByCondition @name = N'%张三%'; -- 按部门和年龄查询,查询部门为技术部且年龄30岁的员工 EXEC GetEmployeeByCondition @department = N'技术部', @age = 30; -- 不传入任何参数,查询所有员工 EXEC GetEmployeeByCondition;
使用sp_executesql的注意事项
- 动态SQL中如果用到表名、字段名等对象名动态拼接,不能用参数化,只能直接拼接字符串,此时需要做好输入校验,避免SQL注入。比如要动态指定表名,需要先判断传入的表名是否在允许的列表中。
- 参数定义中的参数类型和动态SQL中使用的参数类型要一致,否则会出现类型转换错误。
- 动态SQL的执行权限需要单独授予,执行存储过程的用户不一定有执行动态SQL中语句的权限,需要确认权限配置正确。
- 尽量把固定的查询逻辑放在动态SQL外部,只把需要变化的部分放在动态拼接中,减少动态SQL的长度,提升可维护性。
sp_executesql和EXEC直接执行的对比
很多开发者会直接使用EXEC执行拼接好的字符串,比如EXEC('SELECT * FROM Employee WHERE name = ''' + @name + ''''),这种方式存在明显弊端:
- 容易引发SQL注入,如果传入的@name参数是
'' OR 1=1 --,就会查询出所有数据 - 每次执行都会生成新的执行计划,无法复用,性能较差
- 字符串拼接复杂,尤其是包含引号的时候容易出错
而sp_executesql支持参数化,能有效避免上述问题,是执行动态SQL的推荐方式。
动态SQLsp_executesql存储过程SQL_Server修改时间:2026-07-15 08:18:29