Как лучше всего использовать обработку ошибок SQL Server T-SQL? [закрытый]

у нас есть большое приложение, в основном написанное на SQL Server 7.0, где все вызовы базы данных относятся к хранимым процедурам. теперь мы запускаем SQL Server 2005, который предлагает больше возможностей T-SQL.

после почти каждого выбора, вставки, обновления и удаления @@ROWCOUNT и @@ERROR попадают в локальные переменные и оцениваются для проблем. Если есть проблема, делается следующее:

  • выходной параметр сообщения об ошибке set
  • откат (при необходимости) делается
  • информация записывается (вставка) в таблицу журнала
  • возврат с номером ошибки, уникальным для этой процедуры (положительный, если фатальный, отрицательный-предупреждение)

все они не проверяют строки (только когда это известно), а некоторые отличаются более или менее информацией журнала/отладки. Кроме того, логика строк является чем-то, отделенным от логики ошибок (при обновлениях, где поле параллелизма проверяется в предложении 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. Я читал о попробовать...Поймать (язык Transact-SQL) в синтаксис, так что не просто опубликовать резюме. Я ищу любые хорошие идеи и как лучшие сделать или улучшить наши методы обработки ошибок. Это не должно быть Try-Catch, просто любое хорошее или лучшее использование практики ошибки T-SQL обращение.

4 ответов


вы должны прочитать это:

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

Я не могу рекомендовать эту ссылку достаточно высоко. Немного длинновато, но в хорошем смысле.

на фронте есть отказ от ответственности, что он был первоначально написан для SQL Server 2000, но он охватывает новые возможности обработки ошибок try/catch в SQL Server 2005+.


в настоящее время мы используем этот шаблон для любых запросов, которые мы выполняем (вы можете оставить материал транзакции, если он вам не нужен, например, в инструкции 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

конечно, вы можете легко вставить поймал исключение в таблицу журнала ошибок.

Она работает очень хорошо для нас. Вероятно, вы даже можете автоматизировать некоторые преобразования из старых хранимых процессоров в новый формат, используя генерацию кода (например, CodeSmith) или некоторый пользовательский код c#.


нет набора в камне лучших практик для обработки ошибок. Все сводится к тому, что ваши потребности и быть последовательным.

вот пример таблицы и хранимой процедуры, в которой хранятся номера телефонов.

 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

похоже, у вас уже есть очень хорошая ручка. Я подозреваю, что вы делаете больше, чем 95% программистов SQL.

вы должны найти интересную информацию здесь:

один [несвязанное] предложение: начните использовать ' ' вместо '!='.

[* SQL Junkies ушел, поэтому вторая статья недоступна. Я попытаюсь переиздать его где-нибудь и обновить ссылку.]