SQL Server中捕获存储过程RETURN值必须用DECLARE @ret INT后EXEC @ret = proc_name;RETURN仅支持整型且只能一个,多值或非整型应改用OUTPUT参数,结果集需通过INSERT...EXEC+临时表中转。

SQL Server 中用 EXEC @ret = proc_name 捕获返回值不生效?
SQL Server 存储过程的 RETURN 值只能是整型,且必须用 EXEC @ret = proc_name 语法捕获——但很多人写成 SELECT @ret = EXEC proc_name 或漏掉变量声明,直接导致 @ret 为 NULL。返回值不是结果集,也不是 OUTPUT 参数,别混用。
常见错误现象:@ret 始终是 0(哪怕被调用过程写了 RETURN 99),或报错“必须声明标量变量”。
- 必须提前声明接收变量,类型只能是
INT(如DECLARE @result INT) -
EXEC @result = dbo.usp_check_stock是唯一合法语法;SET @result = EXEC ...会报错 - 被调用过程里
RETURN后不能跟表达式(如RETURN @val + 1在某些版本会静默截断为 0) - 若被调用过程未显式
RETURN,默认返回 0
需要多个返回值?改用 OUTPUT 参数更可靠
RETURN 只能传一个整数,真要返回字符串、日期或状态码+消息,OUTPUT 参数才是正解。它不依赖执行顺序,也不受类型限制,还能在过程中多次赋值。
使用场景:主过程调用校验过程,既要返回“是否通过”(bit),又要返回“失败原因”(nvarchar(200))。
- 被调用过程定义:
CREATE PROC usp_validate_order @status BIT OUTPUT, @msg NVARCHAR(200) OUTPUT - 调用方写法:
EXEC usp_validate_order @status OUT, @msg OUT(OUT关键字不可省) - 注意:OUTPUT 参数在调用前可初始化,但被调用过程内部必须显式赋值,否则保持原值(不是 NULL)
- 性能上无额外开销,比临时表或表变量轻量得多
想取结果集当“返回值”?用 INSERT...EXEC 或临时表中转
如果另一个存储过程返回的是查询结果(比如 SELECT id, name FROM users),那它本质上没有“返回值”,只有结果集。SQL Server 不允许直接把结果集赋给变量,得中转。
容易踩的坑:有人试 SELECT @val = col FROM OPENROWSET(...),但要求启用 Ad Hoc Distributed Queries,权限和配置都麻烦。
- 推荐方案:建局部临时表,再
INSERT INTO #tmp EXEC usp_get_active_users - 临时表结构必须与目标过程结果集**严格一致**(列数、顺序、类型),否则报错“列名或数目不匹配”
- 不能用表变量(
DECLARE @t TABLE(...))替代,INSERT INTO @t EXEC ...在 SQL Server 2019 及之前版本不支持 - 若过程含多个结果集,
INSERT...EXEC只取第一个;多结果需改用游标或客户端处理
跨数据库或跨服务器调用?链接服务器 + 四部分命名要注意权限
调用别的库的存储过程(比如从 master 调 ReportDB.dbo.usp_daily_summary),语法不变,但权限模型变了——执行者需在目标库有 EXECUTE 权限,且不能依赖当前库的上下文(比如临时表、会话级 SET 选项)。
若目标在另一台服务器(如 SRV02.ReportDB.dbo.usp_sync),得先配好链接服务器,且注意:
- 远程过程默认以“模拟本地登录”方式执行,若远程服务账户没权限,会报
Msg 7411(服务器未配置为允许 RPC) - 需在链接服务器属性里勾选“RPC”和“RPC Out”
- 四部分名中的数据库名不能是变量;动态拼接要用
sp_executesql,但会失去静态语法检查 - 网络延迟和超时(
remote proc transaction promotion设置不当可能引发分布式事务)
实际组合用法很常见:先用 EXEC @ret = usp_precheck 快速判断能否继续,再用 OUTPUT 拿明细,最后用 INSERT INTO #res EXEC otherdb..usp_report 拿数据。三者职责分明,混用反而难调试。











