SQL Server exec vs sp_executesql

虽然两者都能执行动态 SQL 字符串,但在安全性、性能和功能上存在本质区别。

通常强烈推荐使用 EXEC sp_executesql

核心区别对比

特性 EXEC sp_executesql EXEC(...)
参数化支持 支持(核心优势) 不支持(只能拼字符串)
SQL 注入风险 极低(通过参数隔离) 极高(容易被恶意拼接)
执行计划重用 高(参数变了,SQL 文本不变) 低(参数变了,每次都要重新编译)
输入/输出参数 支持 OUTPUT 参数返回值 无法直接返回变量值
变量作用域 无法直接访问外部变量 无法直接访问外部变量

关键差异详解

1. 安全性:防御 SQL 注入

  • EXEC(危险):必须把变量拼接到字符串里。如果变量包含恶意代码(如 ' ; DROP TABLE Users; --),会直接被执行。
  • sp_executesql(安全):支持参数化查询。变量值只会被当做"数据"处理,绝对不会当做"代码"执行。

2. 性能:缓存执行计划

  • EXEC:每次传入不同的参数,生成的 SQL 字符串都不同(例如 WHERE ID = 1WHERE ID = 2)。SQL Server 必须每次都重新编译。
  • sp_executesql:SQL 文本保持一致(例如 WHERE ID = @id)。SQL Server 只编译一次,后续传入不同参数时直接重用执行计划,大幅提升性能。

3. 功能:支持 OUTPUT 参数

sp_executesql 可以把动态 SQL 执行的结果赋值回外部变量,而 EXEC 做不到。

  • sp_executesql 示例:

    复制代码
    DECLARE @Count INT;
    -- 可以直接通过 @cnt OUTPUT 把值传出来
    EXEC sp_executesql 
        N'SELECT @cnt = COUNT(*) FROM Users WHERE Status = @status',
        N'@status INT, @cnt INT OUTPUT',
        @status = 1, @cnt = @Count OUTPUT;

唯一例外

当需要动态拼接数据库对象名称(如表名、列名)时,两者都无法使用参数化(因为表名不能作为参数)。此时必须使用字符串拼接,但使用 EXEC 的语法通常更简短:

复制代码
-- 这种情况两者都需要注意防范注入,必须用 QUOTENAME()
DECLARE @TableName NVARCHAR(50) = 'Users';
EXEC('SELECT * FROM ' + @TableName); 
相关推荐
zengjuan10051 天前
[特殊字符]️ 松鼠备份 | 数据库备份方案即将重磅上线!三大模式组合拳,精准守护SQL Server每一笔数据
数据库·sqlserver·数据库备份·全量备份·增量备份·差异备份·日志和事务备份
识途老码1 天前
docker运行sqlserver
docker·容器·sqlserver
熊文豪10 天前
SQLServer数据迁移之后,那张报表还能不能秒出
数据库·sqlserver·电科金仓
Alex Gram11 天前
数据库同步工具PanguSync图文教程
java·mysql·postgresql·sqlserver·c#·数据库同步软件·数据库同步工具
哥本哈士奇13 天前
dbt+SQLServer构建数据仓库(10):macro 以及 data vault的应用实例
大数据·数据仓库·sqlserver
哥本哈士奇(aspnetx)14 天前
dbt+SQLServer构建数据仓库(10):macro 以及 data vault的应用实例
sqlserver
哥本哈士奇15 天前
dbt+SQLServer构建数据仓库(8):Vibe Coding用dbt构建data vault数仓
数据仓库·sqlserver
哥本哈士奇(aspnetx)16 天前
dbt+SQLServer构建数据仓库(9):用Vibe Coding创建一个DBT UI
sqlserver
哥本哈士奇(aspnetx)16 天前
dbt+SQLServer构建数据仓库(8):Vibe Coding用dbt构建data vault数仓
sqlserver