必须在声明和调用两端都写 OUTPUT,否则参数不会回传值;声明遗漏则参数为只读输入,调用遗漏则仍按输入处理;结果集未消费完前 OUTPUT 值不可用;NVARCHAR 截断静默发生;OUTPUT 仅适用于固定结构标量值。

必须在声明和调用两端都写 OUTPUT,否则参数根本不会回传值。
声明时漏掉 OUTPUT 关键字
SQL Server 不会默认把同名变量当输出参数——哪怕你只改了存储过程体,没动参数定义,它仍是只读输入。常见于从纯 SELECT 改写为带计算逻辑的存储过程时,只加了赋值语句,却忘了补 OUTPUT。
-
@count INT OUTPUT✅ 正确声明 -
@count INT❌ 调用后@count值不变,哪怕过程里写了SELECT @count = COUNT(*) - 声明和调用是两个独立环节:缺一不可,不能靠“名字对了就自动生效”
调用时忘记 OUTPUT 标记
即使声明正确,调用时不加 OUTPUT,SQL Server 仍按输入参数处理,变量值不会被更新。
-
EXEC usp_GetCount @id = 123, @count = @result OUTPUT✅ -
EXEC usp_GetCount @id = 123, @count = @result❌@result保持原值(可能是NULL或旧值) - 所有
OUTPUT参数必须显式标注,不能省略;多个参数要逐个写,不能只标最后一个
结果集没消费完,OUTPUT 值不可用
如果存储过程中有 SELECT 语句,它的结果集会先发出去;OUTPUT 参数和 RETURN 值要等所有结果集被读取或跳过之后才真正就绪。
- ADO.NET 中用
SqlDataReader:必须反复调用reader.NextResult()直到返回false,再读command.Parameters["@count"].Value - 用
ExecuteScalar():只取第一个结果集的第一行第一列,OUTPUT参数压根不会被填充 - SSMS 里看起来“立刻可见”,是因为它自动遍历了所有结果集;程序里不手动处理,就读不到新值
字符串截断静默发生
NVARCHAR 类型的 OUTPUT 参数如果声明长度太小,SQL Server 不报错,直接截断。
-
@msg NVARCHAR(10)赋值'Operation succeeded'→ 实际只存入'Operation s' - 声明时优先用
NVARCHAR(MAX),尤其不确定长度时 - 调用端(如 ADO.NET 的
SqlParameter.Size)也必须设足够大,否则驱动层可能提前截断 - 调试建议:在存储过程中加
PRINT LEN(@msg)和SELECT @msg AS [Actual]对比确认
OUTPUT 参数只适合返回结构固定、数量明确的标量值;遇到动态列数、不确定行数或嵌套结构,别硬塞——该用结果集就用结果集。










