动态SQL结果存入变量:sp_executesql的进阶用法
今天分享一个动态SQL中的常见问题——如何将EXEC执行的结果存到变量里。
动态SQL执行后,结果只显示在结果窗口,没法直接赋值给变量:
DECLARE @sql NVARCHAR(4000)SET @sql = 'SELECT COUNT(*) FROM users'EXEC(@sql) -- 结果看得见,但拿不到
DECLARE @num INTDECLARE @sql NVARCHAR(4000)SET @sql = 'SELECT @a = COUNT(*) FROM users'-- 执行动态SQL,把结果赋给@numEXEC sp_executesql @sql, N'@a INT OUTPUT', @a = @num OUTPUTSELECT @num AS 结果
DECLARE @count INT, @avgAge INT, @maxAge INTDECLARE @sql NVARCHAR(4000)SET @sql = ' SELECT @a = COUNT(*), @b = AVG(age), @c = MAX(age) FROM users'EXEC sp_executesql @sql, N'@a INT OUTPUT, @b INT OUTPUT, @c INT OUTPUT', @a = @count OUTPUT, @b = @avgAge OUTPUT, @c = @maxAge OUTPUTSELECT @count AS 总数, @avgAge AS 平均年龄, @maxAge AS 最大年龄
-- 创建测试表CREATE TABLE #users (id INT, name VARCHAR(50), age INT)INSERT INTO #users VALUES (1, '张三', 25), (2, '李四', 30), (3, '王五', 28)-- 动态查询:统计年龄大于某个值的记录数DECLARE @minAge INT = 26DECLARE @count INTDECLARE @sql NVARCHAR(4000)SET @sql = 'SELECT @a = COUNT(*) FROM #users WHERE age > @age'EXEC sp_executesql @sql, N'@age INT, @a INT OUTPUT', @age = @minAge, @a = @count OUTPUTSELECT @count AS 年龄大于26的人数 -- 结果:2
-- ❌ 错误EXEC sp_executesql @sql, N'@a INT', @a = @num-- ✅ 正确EXEC sp_executesql @sql, N'@a INT OUTPUT', @a = @num OUTPUT
-- ❌ 错误:用VARCHARDECLARE @sql VARCHAR(4000)-- ✅ 正确:必须用NVARCHARDECLARE @sql NVARCHAR(4000)
-- 注意:@a是SQL内部的变量名,@num是外部的变量名EXEC sp_executesql @sql, N'@a INT OUTPUT', @a = @num OUTPUT-- ↑ ↑-- SQL内部变量名 外部接收变量
CREATE PROCEDURE usp_GetTableCount @tableName NVARCHAR(128)ASBEGIN DECLARE @count INT DECLARE @sql NVARCHAR(4000) SET @sql = 'SELECT @a = COUNT(*) FROM ' + QUOTENAME(@tableName) EXEC sp_executesql @sql, N'@a INT OUTPUT', @a = @count OUTPUT SELECT @count AS 记录数END-- 调用EXEC usp_GetTableCount 'users'
DECLARE @fieldName NVARCHAR(50) = 'salary'DECLARE @total DECIMAL(18,2)DECLARE @sql NVARCHAR(4000)SET @sql = 'SELECT @a = SUM(' + @fieldName + ') FROM employees'EXEC sp_executesql @sql, N'@a DECIMAL(18,2) OUTPUT', @a = @total OUTPUTSELECT @total AS 工资总和
DECLARE @minVal INT, @maxVal INT, @avgVal INTDECLARE @tableName NVARCHAR(50) = 'products'DECLARE @fieldName NVARCHAR(50) = 'price'DECLARE @sql NVARCHAR(4000)SET @sql = ' SELECT @a = MIN(' + @fieldName + '), @b = MAX(' + @fieldName + '), @c = AVG(' + @fieldName + ') FROM ' + QUOTENAME(@tableName)EXEC sp_executesql @sql, N'@a INT OUTPUT, @b INT OUTPUT, @c INT OUTPUT', @a = @minVal OUTPUT, @b = @maxVal OUTPUT, @c = @avgVal OUTPUTSELECT @minVal AS 最小值, @maxVal AS 最大值, @avgVal AS 平均值
NVARCHAR | |
QUOTENAME包裹 |
-- 通用模板:单值返回DECLARE @result INTDECLARE @sql NVARCHAR(4000)SET @sql = 'SELECT @a = COUNT(*) FROM 你的表名 WHERE 条件'EXEC sp_executesql @sql, N'@a INT OUTPUT', @a = @result OUTPUTSELECT @result-- 通用模板:多值返回DECLARE @r1 INT, @r2 VARCHAR(50)DECLARE @sql NVARCHAR(4000)SET @sql = 'SELECT @a = 字段1, @b = 字段2 FROM 你的表名 WHERE 条件'EXEC sp_executesql @sql, N'@a INT OUTPUT, @b VARCHAR(50) OUTPUT', @a = @r1 OUTPUT, @b = @r2 OUTPUTSELECT @r1, @r2
sp_executesqlOUTPUT | |
QUOTENAME防注入 |
一句话:EXEC拿不到结果,用sp_executesql配合OUTPUT参数就能轻松搞定!
阅读原文:原文链接