如何在sql server中重新抛出相同的异常

时间:2010-03-20 00:03:09

标签: sql sql-server database tsql exception-handling

我想在我的try块中出现的sql server中重新抛出相同的异常。 我可以抛出相同的消息,但我想抛出相同的错误。

BEGIN TRANSACTION
    BEGIN TRY
            INSERT INTO Tags.tblDomain 
            (DomainName, SubDomainId, DomainCode, Description)
            VALUES(@DomainName, @SubDomainId, @DomainCode, @Description)
            COMMIT TRANSACTION
    END TRY

    BEGIN CATCH
            declare @severity int; 
            declare @state int;

            select @severity=error_severity(), @state=error_state();

            RAISERROR(@@Error,@ErrorSeverity,@state);
            ROLLBACK TRANSACTION
    END CATCH
  

RAISERROR(@@Error, @ErrorSeverity, @state);

此行将显示错误,但我想要类似的功能。 这会引发错误,错误编号为50000,但我希望抛出错误号码,我正在通过@@error

我想在前端

捕获此错误

即。

catch (SqlException ex)
{
if ex.number==2627
MessageBox.show("Duplicate value cannot be inserted");
}

我想要这个功能。使用raiseerror无法实现。我不想在后端提供自定义错误消息。

RAISEERROR应该在我将错误消息传递给catch

时返回下面提到的错误
Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert,
     

第14行       违反UNIQUE KEY约束'UK_DomainCode'。无法插入   对象中的重复键   'Tags.tblDomain'。       声明已经终止。

修改

如果我希望在前端处理异常,考虑到存储过程包含需要执行的多个查询,那么不使用try catch块会有什么缺点

10 个答案:

答案 0 :(得分:114)

SQL 2012引入了throw语句:

http://msdn.microsoft.com/en-us/library/ee677615.aspx

  

如果指定了没有参数的THROW语句,则必须出现   在CATCH块内。这会导致捕获的异常被引发。

BEGIN TRY
    BEGIN TRANSACTION
    ...
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    THROW
END CATCH

答案 1 :(得分:102)

这是一个功能齐全的干净代码示例,用于在发生错误时回滚一系列语句并报告错误消息。

begin try
    begin transaction;

    ...

    commit transaction;
end try
begin catch
    declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
    select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
    rollback transaction;
    raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
end catch

答案 2 :(得分:5)

重新引出CATCH块(SQL 2012之前的代码,对SQL 2012及更高版本使用THROW语句):

DECLARE
    @ErrorMessage nvarchar(4000) = ERROR_MESSAGE(),
    @ErrorNumber int = ERROR_NUMBER(),
    @ErrorSeverity int = ERROR_SEVERITY(),
    @ErrorState int = ERROR_STATE(),
    @ErrorLine int = ERROR_LINE(),
    @ErrorProcedure nvarchar(200) = ISNULL(ERROR_PROCEDURE(), '-');
SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + @ErrorMessage;
RAISERROR (@ErrorMessage, @ErrorSeverity, 1, @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine)

答案 3 :(得分:4)

我认为您的选择是:

  • 不要发现错误(让它冒泡)
  • 提出自定义

在某些时候,SQL可能会引入一个重新加载命令,或者只捕获某些错误的能力。但是现在,使用一种解决方法。遗憾。

答案 4 :(得分:1)

你不能:只有引擎可以抛出少于50000的错误。你所能做的就是抛出一个看起来的异常......

See my answer here please

这里的提问者使用客户端交易做他想做的事情,我认为这有点傻了......

答案 5 :(得分:0)

好的,这是一种解决方法......: - )

DECLARE @Error_Number INT
BEGIN TRANSACTION 
    BEGIN TRY
    INSERT INTO Test(Id, Name) VALUES (newID(),'Ashish') 
    /* Column 'Name' has unique constraint on it*/
    END TRY
    BEGIN CATCH

            SELECT ERROR_NUMBER()
            --RAISERROR (@ErrorMessage,@Severity,@State)
            ROLLBACK TRAN
    END CATCH

如果你注意到catch块,它不会引发错误,而是返回实际的错误号(并且还会回滚事务)。现在在您的.NET代码中,而不是捕获 例外,如果您使用ExecuteScalar(),您将获得所需的实际错误编号并显示相应的编号。

int errorNumber=(int)command.ExecuteScalar();
if(errorNumber=<SomeNumber>)
{
    MessageBox.Show("Some message");
}

希望这有帮助,

编辑: - 只需注意,如果您想获取受影响的记录数并尝试使用ExecuteNonQuery,上述解决方案可能对您无效。否则,我认为它适合你的需要。请告诉我。

答案 6 :(得分:0)

在发生错误后停止在存储过程中执行并将错误冒回到调用程序的方法是遵循可能使用此代码引发错误的每个语句:

If @@ERROR > 0
Return

我很惊讶自己发现存储过程中的执行可能会在发生错误后继续 - 没有意识到这会导致一些难以追踪的错误。

这种类型的错误处理并行(pre .Net)Visual Basic 6.期待SQL Server 2012中的Throw命令。

答案 7 :(得分:0)

鉴于您尚未迁移到2012年,实现原始错误代码冒泡的一种方法是使用您从catch块抛出的异常的文本消息部分。请记住,它可以包含一些结构,例如,用于调用者代码的XML文本,以便在其catch块中进行解析。

答案 8 :(得分:0)

当您希望在事务中执行SQL语句并将错误提供给代码时,您还可以为这些方案创建包装器存储过程。

CREATE PROCEDURE usp_Execute_SQL_Within_Transaction
(
    @SQL nvarchar(max)
)
AS

SET NOCOUNT ON

BEGIN TRY
    BEGIN TRANSACTION
        EXEC(@SQL)
    COMMIT TRANSACTION
END TRY

BEGIN CATCH
    DECLARE @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int
    SELECT @ErrorMessage = N'Error Number: ' + CONVERT(nvarchar(5), ERROR_NUMBER()) + N'. ' + ERROR_MESSAGE() + ' Line ' + CONVERT(nvarchar(5), ERROR_LINE()), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE()
    ROLLBACK TRANSACTION
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState)
END CATCH

GO

-- Test it
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1/0; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'EXEC usp_Another_SP'

答案 9 :(得分:-3)

从设计的角度来看,使用原始错误号和自定义消息抛出异常有什么意义?在某种程度上,它破坏了应用程序和数据库之间的接口契约。 如果要捕获原始错误并在更高的代码中处理它们,请不要在数据库中处理它们。然后,当您捕获异常时,您可以将呈现给用户的消息更改为您想要的任何内容。我不会这样做,因为它使你的数据库代码嗯'不对'。正如其他人所说,你应该定义一组你自己的错误代码(50000以上)并改为抛出它们。然后,您可以将潜在的业务问题(“邮政编码无效”,“未找到符合条件的行”等)分开处理完整性问题(“不允许重复值”),等等。