如何在存储过程调用 RAISERROR 之前读取存储过程设置的批处理中的 OUTPUT 参数?



在.net中,当我使用SqlCommand并调用使用RAISERROR抛出的存储过程时,我可以读取该过程设置的输出变量。但是,从 T-SQL 批处理中调用该过程时,似乎根本没有设置OUTPUT参数。

当过程调用RAISERROR以指示错误时,如何从调用 T-SQL 中读取OUTPUT参数@OUT_x

CREATE PROCEDURE dbo.p_x (
@OUT_x INT OUTPUT
) AS
BEGIN
SET @OUT_x = 1;
SELECT 'inner ', @OUT_x;
RAISERROR('yo', 11, 1);
END
GO
DECLARE @x INT = 0;
SELECT 'initial', @x;
BEGIN TRY
EXEC dbo.p_x @x OUTPUT;
END TRY
BEGIN CATCH
SELECT 'catch', @x;
END CATCH
SELECT 'end', @x;

输出:

------- -----------
initial 0

------ -----------
inner  1

----- -----------
catch 0

---- -----------
end  0

我通过在通过RAISERROR抛出之前在存储过程中显示其值来证明@OUT_x设置为1。我希望"catch"和"end"条目1,但就好像参数值复制仅在存储过程成功完成时发生OUTPUT。我知道当我通过SqlCommand调用存储过程时,我可以读取这些OUTPUT参数 - 为什么 T-SQL 批处理必须有所不同?

是否有不涉及更改存储过程的解决方法?我真的希望我的存储过程在特殊情况下抛出(RAISERROR),但我还需要使用OUTPUT参数来传达错误详细信息。

这是TRY .. CATCH工作方式的限制:当发生RAISERROR并输入CATCH块时,它不会将输出参数的值复制回相应的局部变量。对此有要说(结果可能不被依赖,因为毕竟发生了错误),但这同样可能是疏忽。对于初学者来说,不会尝试忽略存储过程在此之前生成的任何结果集(并将结果放入其中是一种可能的解决方法,但它需要更改存储过程)。据我所知,这种行为没有正式记录,因为T-SQL错误处理的许多事情都没有,但Erland Sommarskog的优秀文章提到了它。

由于结果的传输方式,客户端代码不会遇到相同的问题:分配输出参数时,将发回 TDS 数据包以指示;当发生错误时,将发回另一个 TDS 数据包以指示这一点。这些事情按时间顺序发生。客户端必须在出错时主动丢弃输出参数,而不仅仅是像TRY .. CATCH那样忘记复制它们。

如果仅使用 输出参数来传达错误详细信息,请考虑不使用RAISERROR而是使存储过程返回客户端处理的错误代码。当然,您冒着外部客户忘记检查这一点的风险,因为他们实际上不会得到SqlException。另一种选择是在传递给RAISERROR的消息中对所需的所有详细信息进行编码,或者如果它是小整数,则在ERROR_STATE()中编码。这仍然需要修改存储过程;只要您打算使用TRY .. CATCH,就没有办法解决这个问题。

最新更新