从 SQL Server 函数向存储过程抛出异常
Posted
技术标签:
【中文标题】从 SQL Server 函数向存储过程抛出异常【英文标题】:Throw exception from SQL Server function to stored procedure 【发布时间】:2013-03-28 00:51:19 【问题描述】:我在 SQL Server 2012 中有存储过程说 spXample 和一个缩放器值函数说 fXample。
我从 spXample 调用函数 fXample。
我可以在函数中抛出异常并在存储过程的 Catch
块中捕获它并重新抛出到调用 C# 代码吗?
更新:
我写的函数是这样的:
CREATE FUNCTION dbo.fXample(@i INT)
RETURNS TINYINT
AS
BEGIN
RETURN (SELECT CASE WHEN @i < 10
THEN THROW 51000,'Xample Exception',1;
ELSE (SELECT @i)
END);
END
GO
我遇到错误
消息 443,级别 16,状态 14,过程 fXample,第 46 行无效使用 函数中的副作用运算符“THROW”。
如何编写替代代码来实现上述功能?
【问题讨论】:
根据什么类型的条件在函数中抛出异常?以及什么类型的函数,标量、TVF、多语句TVF? 尝试 RAISERROR:msdn.microsoft.com/en-us/library/ms178592.aspx。如果你给它一个较低的严重性,它可以被 CATCH 捕获。从那里,您可以使用“严重”严重性(我认为 11+;页面上有示例)调用它,它将停止 SP 的执行并将其踢回您的应用程序。 Aaron 它的定标器值,异常将基于验证。 这里没有 2012 年可供测试,但我认为你不能。如果你尝试会发生什么?在以前的版本中,尝试使用RAISERROR
会失败并显示 Invalid use of a side-effecting operator 'RAISERROR' within a function.
@AaronBertrand 请参阅***.com/questions/1485034/… 如何处理此问题(+1 指出如何发布此类信息)
【参考方案1】:
您可以通过在验证失败时强制出现错误条件来做到这一点,前提是这不是可能自然发生的错误。当您知道某个错误只能在验证失败时发生时,您可以通过检查 catch 块中的 error_number 以自定义方式处理该错误。 tempdb 中的示例:
USE tempdb;
GO
CREATE FUNCTION dbo.fXample(@i INT)
RETURNS TINYINT
AS
BEGIN
RETURN (SELECT CASE WHEN @i < 10 -- change this to your "validation failed" condition
THEN 1/0 -- something that will generate an error
ELSE (SELECT @i) -- (you'd have your actual retrieval code here)
END);
END
GO
CREATE PROCEDURE dbo.spXample
@i INT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
SELECT dbo.fXample(@i);
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 8134 -- divide by zero
BEGIN
THROW 50001, 'Your custom error message.', 1;
-- you can throw any number > 50000 here
END
ELSE -- something else went wrong
BEGIN
THROW; -- throw original error
END
END CATCH
END
GO
现在试试吧:
EXEC dbo.spXample @i = 10; -- works fine
EXEC dbo.spXample @i = 6; -- fails validation
EXEC dbo.spXample @i = 256; -- passes validation but overflows return
结果:
----
10
消息 50001,级别 16,状态 1,过程 spXample,第 12 行 您的自定义错误消息。
消息 220,级别 16,状态 2,过程 spXample,第 7 行 数据类型 tinyint 的算术溢出错误,值 = 256。
【讨论】:
谢谢,但是我们必须跳多可笑的圈子才能做一些如此简单的事情!只是表明 SQL 是一种过去的语言,它根本不会继续前进。 :( 我只想添加 THROW 来自 Sql Server 2012 @NickG,您可以改用存储过程。函数实际上是一种优雅的方式来表示它们没有副作用。当然,它们仍然可以,因为您可以抛出内置错误 - 所以它们没有副作用并不完全正确。这是一种在语言中拥有纯函数的优雅方式。 SQL 是一门远远领先于时代的语言,考虑到它的历史,它令人印象深刻。它基本上是基于集合的数据的 DSL。太棒了!【参考方案2】:/* *** EXAMPLES ***
SELECT dbo.fnDoSomething(500) -- RETURNS TRUE
SELECT dbo.fnDoSomething(5000) -- THROWS ERROR
Msg 245, Level 16, State 1, Line 4
Conversion failed when converting the varchar value
'Function Error - [dbo].[fnDoSomething] - Value is greater than 1000' to data type bit.
*/
CREATE FUNCTION dbo.fnDoSomething
(
@SomeValue INT
)
RETURNS BIT
AS
BEGIN
IF @SomeValue > 1000
BEGIN
DECLARE @FunctionName AS SYSNAME = QUOTENAME(OBJECT_SCHEMA_NAME(@@PROCID)) + '.' + QUOTENAME(OBJECT_NAME(@@PROCID))
DECLARE @Error AS VARCHAR(200) = 'Function Error - '+ @FunctionName + ' - Value is greater than 1000'
RETURN CONVERT(BIT,@Error)
END
RETURN 1
END
GO
【讨论】:
我喜欢它!和this answer一样丑【参考方案3】:虽然这个问题已经解决了,但我觉得我还是应该分享这个。我认为这将是一个更简单的解决方案:
BEGIN TRY
THROW 60000, 'Error', 1;
END TRY
BEGIN CATCH
THROW
END CATCH
【讨论】:
Msg 443,级别 16,状态 14,过程测试,第 3 行 [批处理开始第 57 行] 在函数中无效使用副作用运算符“BEGIN TRY” .消息 443,级别 16,状态 14,过程测试,第 7 行 [批处理开始第 57 行] 在函数中无效使用副作用运算符“THROW”。消息 443,级别 16,状态 14,过程测试,第 5 行 [批处理开始第 57 行] 在函数中无效使用副作用运算符“END TRY”。消息 443,级别 16,状态 14,程序测试,第 6 行 [批处理开始第 57 行] 在乐趣中无效使用副作用运算符“BEGIN CATCH”......【参考方案4】:如果满足特定条件,您也可以在存储过程中引发错误,但错误严重性必须高于 10 才能引发 SQL 异常
CREATE PROC test
AS
RAISERROR('the error message', 11, 1);
RETURN
您可以稍后在您的 catch 块中访问此消息
try
testProc.ExecuteNonQuery();
catch(SqlException ex)
System.Diagnostics.Debug.WriteLine(ex.Message);
输出:“错误信息”
【讨论】:
以上是关于从 SQL Server 函数向存储过程抛出异常的主要内容,如果未能解决你的问题,请参考以下文章
使用实体框架迁移时 SQL Server 连接抛出异常 - 添加代码片段