【问题标题】:What is the best practice use of SQL Server T-SQL error handling? [closed]SQL Server T-SQL 错误处理的最佳实践是什么? [关闭]
【发布时间】:2010-10-18 01:47:32
【问题描述】:

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

在几乎每次 SELECT、INSERT、UPDATE 和 DELETE 之后,@@ROWCOUNT 和 @@ERROR 都会被捕获到局部变量中并评估问题。如果出现问题,请执行以下操作:

  • 错误信息输出参数已设置
  • 回滚(如有必要)已完成
  • 信息已写入(插入)到日志表中
  • 返回一个错误号,对于这个过程是唯一的(如果是致命的,则为正,如果是警告,则为负)

它们都不检查行(仅在已知时),并且有些不同或多或少的日志/调试信息。此外,行逻辑有时会从错误逻辑中分离出来(在 WHERE 子句中检查并发字段的更新中,rows=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

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

【问题讨论】:

    标签: sql-server sql-server-2005 tsql error-handling


    【解决方案1】:

    您应该阅读以下内容:

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

    我不能高度推荐该链接。有点长,但是很好。

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

    【讨论】:

    • 我觉得它跳过了 SQL Server 2005 的东西,但无论如何都很棒。他网站的其他部分也是如此。
    • 对于 SQL Server 2005+ 从这里开始sommarskog.se/error_handling/Part1.html
    【解决方案2】:

    看起来你已经很好地处理了这个问题。我怀疑你在做超过 95% 的 SQL 程序员。

    你应该在这里找到一些有趣的信息:

    一个 [不相关的] 建议:开始使用 '' 而不是 '!='。

    [* SQL Junkies 已经消失,因此第二篇文章不可用。我会尝试在某处重新发布并更新链接。]

    【讨论】:

    • 为什么使用 '' 而不是 '!='?
    • ANSI 规范指定 。许多 db 也支持 !=,但这不是标准的。
    • 这只是一个建议。嘘。
    • 但既然你知道了,为什么不做对呢?...
    • 您可能需要将您的 SQL 2000 代码移植到 SQL 2005 或 SQL 2008。MS 在线书籍中写道,许多功能将被弃用并最终被删除。砧板上的许多是非 ANSI 扩展。也就是说,我同意,你不应该编写期望移植到 DB2 的代码,因为它永远不会发生。
    【解决方案3】:

    对于错误处理,没有一成不变的最佳实践。这一切都取决于您的需求和一致性。

    这是一个存储电话号码的表和存储过程的示例。

     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
    

    【讨论】:

      【解决方案4】:

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

      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
      

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

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

      【讨论】:

      • 为什么要在TRY块外开始交易,有什么好处吗? MSDN 上的所有示例都将 BEGIN TRAN 显示为 TRY technet.microsoft.com/en-us/library/… 中的第一条语句
      • 如果使用事务,还应考虑 XACT_STATE:msdn.microsoft.com/en-us/library/ms189797.aspx
      • 当发生错误并将控制权传递给 CATCH 块时,在执行 CATCH 块之后,控制权会传递回导致错误并因此继续执行 COMMIT TRANSACTION 的语句之后的语句,或者在 CATCH 块之后传递给语句的控件?
      • @user8810865: 否 - 执行在 CATCH 块之后继续。
      猜你喜欢
      • 1970-01-01
      • 2014-03-25
      • 1970-01-01
      • 1970-01-01
      • 2014-11-28
      • 2011-09-22
      • 1970-01-01
      • 2013-01-13
      • 2020-06-05
      相关资源
      最近更新 更多