Jaka jest najlepsza praktyka obsługi błędów SQL Server T-SQL? [zamknięte]

Mamy dużą aplikację napisaną głównie w SQL Server 7.0, gdzie wszystkie wywołania bazy danych są do procedur składowanych. obecnie uruchamiamy SQL Server 2005 , który oferuje więcej funkcji T-SQL.

Po prawie każdym SELECT, INSERT, UPDATE I DELETE, @@ROWCOUNT i @ @ ERROR są przechwytywane do zmiennych lokalnych i oceniane pod kątem problemów. W przypadku wystąpienia problemu wykonujemy następujące czynności:

  • parametr wyjściowy Komunikatu o błędzie jest ustawiony
  • rollback (w razie potrzeby) jest done
  • info jest zapisywane (INSERT) do tabeli log
  • powrót z numerem błędu, unikalnym dla tej procedury (dodatni, jeśli śmiertelny, ujemny to Ostrzeżenie)

Wszystkie nie sprawdzają wierszy (tylko wtedy, gdy są znane), a niektóre różnią się mniej lub bardziej informacjami log/debug. Również logika rows jest oddzielona od logiki błędu(przy aktualizacjach, gdzie pole współbieżności jest zaznaczone w klauzuli WHERE, rows=0 oznacza, że ktoś inny zaktualizował dane). Jednak tutaj jest dość ogólny przykład:

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

Szukam zastąpienia tego, jak to robimy, try-CATCH T-SQL. Czytałem o próbie ...CATCH (Transact-SQL) składnia, więc nie po prostu pisać jakieś podsumowanie tego. Szukam dobrych pomysłów i jak najlepiej zrobić lub poprawić nasze metody obsługi błędów. Nie musi to być Try-Catch, tylko dobre lub najlepsze wykorzystanie obsługi błędów T-SQL.

Author: SteveC, 2009-04-07

4 answers

Powinieneś przeczytać to:

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

Nie polecam tego linku. Jest trochę długa, ale w dobry sposób.

Z przodu znajduje się zastrzeżenie, że pierwotnie został napisany dla SQL Server 2000, ale obejmuje również nowe możliwości obsługi błędów try/catch w SQL Server 2005+.

 31
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-08 17:56:01

Obecnie używamy tego szablonu dla wszelkich zapytań, które wykonujemy (możesz pominąć transakcję, jeśli nie potrzebujesz jej np. w instrukcji 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

Oczywiście możesz łatwo wstawić przechwycony wyjątek do tabeli dziennika błędów.

To działa naprawdę dobrze dla nas. Prawdopodobnie możesz nawet zautomatyzować część konwersji ze starych przechowywanych procesorów do nowego formatu za pomocą generowania kodu (np. CodeSmith) lub niestandardowego kodu C#.
 19
Author: marc_s,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-07 09:20:59

Nie ma zestawu najlepszych praktyk stone do obsługi błędów. Wszystko sprowadza się do tego, jakie są Twoje potrzeby i bycia konsekwentnym.

Oto przykład tabeli i procedury składowanej, która przechowuje numery telefonów.

 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
 6
Author: DBAndrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-07 15:25:16

Wygląda na to, że dobrze sobie z tym radzisz. Podejrzewam, że robisz więcej niż 95% programistów SQL tam.

Tutaj znajdziesz kilka ciekawych informacji:

Jedna [niezwiązana] sugestia: zacznij używać ' ' zamiast '!='.

[*SQL Junkies odszedł, więc drugi artykuł nie jest dostępny. Postaram się to gdzieś opublikować i zaktualizować link.]

 3
Author: Rob Garrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-30 17:53:42