SQL Server T-SQL错误处理的最佳实践使用是什么?

KM.*_*KM. 35 t-sql sql-server error-handling sql-server-2005

我们有一个大型应用程序,主要用SQL Server 7.0编写,其中所有数据库调用都是存储过程.我们现在运行SQL Server 2005,它提供了更多的T-SQL功能.

在几乎所有SELECT,INSERT,UPDATE和DELETE之后,@@ ROWCOUNT和@@ ERROR被捕获到局部变量中并对问题进行评估.如果出现问题,则执行以下操作:

  • 错误消息输出参数已设置
  • 回滚(如有必要)完成
  • info被写入(INSERT)到日志表
  • 返回错误编号,此程序是唯一的(如果是致命的,则是肯定的,否定是警告)

它们都不检查行(只有在知道的情况下),并且一些不同的日志/调试信息或多或少.此外,行逻辑是从错误逻辑拆分的某些时间(在更新中,在WHERE子句中检查并发字段,行= 0表示其他人已更新数据).但是,这是一个相当通用的例子:

SELECT, INSERT, UPDATE, or DELETE

SELECT @Error=@@ERROR, @Rows=@@ROWCOUNT
IF @Rows!=1 OR @Error!=0
BEGIN
    SET @ErrorMsg='ERROR 20, ' + ISNULL(OBJECT_NAME(@@PROCID), 'unknown') 
                               + ' - unable to ???????? the ????.'
    IF @@TRANCOUNT >0
    BEGIN 
        ROLLBACK
    END

    SET @LogInfo=ISNULL(@LogInfo,'')+'; '+ISNULL(@ErrorMsg,'')+
        + ' @YYYYY='        +dbo.FormatString(@YYYYY)
        +', @XXXXX='        +dbo.FormatString(@XXXXX)
        +', Error='         +dbo.FormatString(@Error)
        +', Rows='          +dbo.FormatString(@Rows)

    INSERT INTO MyLogTable (...,Message) VALUES (....,@LogInfo)

    RETURN 20

END
Run Code Online (Sandbox Code Playgroud)

我正在考虑用TRY-CATCH T-SQL替换我们如何做到这一点.我已经阅读了TRY ... CATCH(Transact-SQL)语法,所以不要只发布一些摘要.我正在寻找任何好的想法,以及如何最好地做或改进我们的错误处理方法.它不一定是Try-Catch,只是任何使用T-SQL错误处理的好或最佳实践.

Joe*_*orn 31

你应该读这个:

http://www.sommarskog.se/error-handling-I.html

我不能足够推荐这个链接.这有点长,但是好的方式.

前面有一个免责声明,它最初是为SQL Server 2000编写的,但它也涵盖了SQL Server 2005+中新的try/catch错误处理能力.

  • 对于SQL Server 2005+,请从http://www.sommarskog.se/error_handling/Part1.html开始 (2认同)

mar*_*c_s 24

我们目前将此模板用于我们执行的任何查询(如果您在例如DDL语句中不需要它,则可以省略事务内容):

BEGIN TRANSACTION
BEGIN TRY
    // do your SQL statements here

    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

    ROLLBACK TRANSACTION
END CATCH
Run Code Online (Sandbox Code Playgroud)

当然,您可以轻松地将捕获的异常插入到错误日志表中.

它对我们来说非常有效.您甚至可以使用代码生成(例如CodeSmith)或一些自定义C#代码自动执行从旧存储过程到新格式的一些转换.

  • 为什么在TRY区块之外开始交易,有优势吗?MSDN上的所有示例都显示BEGIN TRAN是TRY中的第一个语句http://technet.microsoft.com/en-us/library/ms179296%28v=sql.105%29.aspx (10认同)
  • 如果使用交易,也应考虑XACT_STATE:https://msdn.microsoft.com/en-us/library/ms189797.aspx (4认同)
  • @user8810865:否 - 执行继续*在`CATCH`块之后。 (2认同)

DBA*_*rew 6

错误处理没有一套完整的最佳实践.这一切都归结为您的需求和一致性.

以下是存储电话号码的表和存储过程的示例.

 SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    SET ANSI_PADDING ON
    GO
    CREATE TABLE [dbo].[Phone](
        [ID] [int] IDENTITY(1,1) NOT NULL,
        [Phone_Type_ID] [int] NOT NULL,
        [Area_Code] [char](3) NOT NULL,
        [Exchange] [char](3) NOT NULL,
        [Number] [char](4) NOT NULL,
        [Extension] [varchar](6) NULL,
     CONSTRAINT [PK_Phone] PRIMARY KEY CLUSTERED 
    (
        [ID] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    GO
    SET ANSI_PADDING OFF
    GO
    /**/

    CREATE PROCEDURE [dbo].[usp_Phone_INS]
         @Customer_ID INT
        ,@Phone_Type_ID INT
        ,@Area_Code CHAR(3)
        ,@Exchange CHAR(3)
        ,@Number CHAR(4)
        ,@Extension VARCHAR(6)
    AS
    BEGIN
        SET NOCOUNT ON;

        DECLARE @Err INT, @Phone_ID INT

        BEGIN TRY
            INSERT INTO Phone
                (Phone_Type_ID, Area_Code, Exchange, Number, Extension)
            VALUES
                (@Phone_Type_ID, @Area_Code, @Exchange, @Number, @Extension)
            SET @Err = @@ERROR
            SET @Phone_ID = SCOPE_IDENTITY()
            /* 
                Custom error handling expected by the application.
                If Err = 0 then its good or no error, if its -1 or something else then something bad happened.
            */
            SELECT ISNULL(@Err,-1) AS Err, @Phone_ID
        END TRY
        BEGIN CATCH
            IF (XACT_STATE() <> 0)
                BEGIN
                    ROLLBACK TRANSACTION
                END

            /* 
                Add your own custom error handling here to return the passed in paramters. 
                I have removed my custom error halding code that deals with returning the passed in parameter values.
            */

            SELECT ERROR_NUMBER() AS Err, ISNULL(@Phone_ID,-1) AS ID
        END CATCH
    END
Run Code Online (Sandbox Code Playgroud)