Práce s chybami pomocí TRY...CATCH

Dokončeno

Návod

Další podrobnosti najdete na kartě Text a obrázky .

Produkční databázové aplikace musí řádně zpracovávat neočekávané situace. Dělení nulou, porušení omezení, vypršení časového limitu připojení a neplatná data můžou způsobit chyby. Neošetřené chyby mají za následek nejasné chybové zprávy, neúplné transakce nebo chybové ukončení aplikace. Správné zpracování chyb zajišťuje, že kód T-SQL selže předvídatelně a poskytuje smysluplnou zpětnou vazbu.

Databázové operace pracují s více uživateli, externími systémy a nepředvídatelnými vstupy dat současně. Na rozdíl od kódu aplikace, který se může zotavit z neúspěšné operace opakováním, můžou chyby databáze ponechat data v nekonzistentním stavu, s některými vloženými řádky a jinými ne nebo s zámky uchovávanými po neomezenou dobu. Zpracování chyb transformuje tyto chaotické režimy selhání do řízených, předvídatelných reakcí.

Dobře navržené zpracování chyb zlepšuje kód několika způsoby:

  • Ochrana integrity dat: Když se operace přeruší v průběhu, správné zpracování chyb zajistí, že buď budou všechny změny potvrzeny společně, nebo žádná z nich nezůstane zachována. Bez toho může vícekrokový proces opustit databázi s osamocenými záznamy, nesouhlasícími součty nebo poškozenými relacemi.

  • Efektivita ladění: Zachytávání podrobností o chybě, jako je číslo řádku, název procedury a konkrétní chybová zpráva, urychlí řešení potíží. Místo prohledávání protokolů o vágních selhání můžete přesně určit, kde a proč došlo k chybě.

  • Uživatelské prostředí: Aplikace můžou místo chyb kryptografických databází zobrazovat smysluplné zprávy, například "ID produktu neexistuje". To pomáhá uživatelům pochopit, co se nepovedlo a jak ho opravit.

  • Provozní viditelnost: Protokolování chyb do vyhrazené tabulky vytvoří záznam auditu, který pomáhá identifikovat vzory, jako jsou opakovaná porušení omezení označující chyby nebo chyby časového limitu, které naznačují problémy s výkonem.

  • Ladný pokles funkčnosti: Pokud jedna operace selže, zpracování chyb umožňuje zbývající část kódu pokračovat nebo provést alternativní akci, aniž by došlo k chybě celé dávky nebo uložené procedury.

Implementace zpracování chyb T-SQL

T-SQL poskytuje strukturované zpracování chyb prostřednictvím TRY...CATCH bloků, podobně jako zpracování výjimek v jiných programovacích jazycích. Pokud dojde k chybě v TRY bloku, provádění se přenese do CATCH bloku, kde můžete chybu správně zpracovat:

BEGIN TRY
    -- TRY block contains code that might cause an error
    -- If an error occurs here, execution jumps to the CATCH block
    SELECT 1/0;  -- This causes a division by zero error
END TRY
BEGIN CATCH
    -- CATCH block handles the error
    -- This code runs only if an error occurred in the TRY block
    PRINT 'An error occurred';
END CATCH;

Bez zpracování chyb by stejný kód ukončil chybovou zprávou:

SELECT 1/0;  -- Msg 8134: Divide by zero error encountered

Poznámka:

TRY...CATCH nemůže zachytit všechny chyby. Chyby kompilace (chyby syntaxe, chybějící objekty) a chyby se závažností 20 nebo vyšší, které ukončí připojení, nelze zachytit ve stejné sessí.

Načtení informací o chybě

CATCH V rámci bloku sql Server poskytuje následující funkce pro načtení podrobností o chybě, ke které došlo:

Funkce Description
ERROR_NUMBER() Vrátí číslo chyby.
ERROR_MESSAGE() Vrátí celý text chybové zprávy.
ERROR_SEVERITY() Vrátí závažnost chyby (0–25).
ERROR_STATE() Vrátí číslo chybového stavu.
ERROR_LINE() Vrátí číslo řádku, kde došlo k chybě.
ERROR_PROCEDURE() Vrátí název uložené procedury nebo triggeru.

Následující příklad ukazuje, jak zachytit podrobnosti o chybě a protokolovat je do tabulky pro pozdější analýzu:

BEGIN TRY
    -- Attempt an operation that might fail
    INSERT INTO SalesLT.Customer (CustomerID, FirstName, LastName)
    VALUES (1, 'Test', 'Customer');  -- Duplicate key causes error
END TRY
BEGIN CATCH
    -- Log error details to a table using the ERROR_* functions
    INSERT INTO ErrorLog (
        ErrorTime,
        ErrorNumber,
        ErrorSeverity,
        ErrorState,
        ErrorProcedure,
        ErrorLine,
        ErrorMessage
    )
    VALUES (
        GETDATE(),
        ERROR_NUMBER(),       -- The error number (e.g., 2627 for duplicate key)
        ERROR_SEVERITY(),     -- Severity level (0-25)
        ERROR_STATE(),        -- Error state for debugging
        ISNULL(ERROR_PROCEDURE(), 'Ad hoc query'),  -- NULL if not in a procedure
        ERROR_LINE(),         -- Line number where error occurred
        ERROR_MESSAGE()       -- Full error message text
    );

    -- Re-raise the error to the calling application
    THROW;
END CATCH;

Návod

Před opětovnou reraizací vždy protokolujte chyby. Po použití THROW nebo RAISERRORfunkce chyby se vrátí NULL , pokud jsou znovu volána.

Zpracování transakcí pomocí TRY...CATCH

Pokud dojde k chybám v rámci transakcí, musíte explicitně vrátit nepotvrzené operace zpět. Funkce @@TRANCOUNT vám řekne, jestli je transakce aktivní:

BEGIN TRY
    -- Start a transaction to group multiple operations
    BEGIN TRANSACTION;

    -- First operation: update product prices
    UPDATE SalesLT.Product
    SET ListPrice = ListPrice * 1.05
    WHERE ProductCategoryID = 5;

    -- Second operation: update order totals
    -- If this fails, we want to undo the first update too
    UPDATE SalesLT.SalesOrderHeader
    SET TotalDue = TotalDue * 1.05
    WHERE CustomerID = 12345;

    -- Both operations succeeded, make changes permanent
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Check if a transaction is still active before rolling back
    -- Some errors auto-rollback, so @@TRANCOUNT might be 0
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;  -- Undo all changes from this transaction

    -- Re-raise the error so the caller knows something failed
    THROW;
END CATCH;

Kontrola @@TRANCOUNT je důležitá, protože:

  • K chybě může dojít před BEGIN TRANSACTION
  • Některé chyby automaticky vrátí transakci před dosažením CATCH.
  • Pokus o vrácení zpět bez aktivní transakce způsobí jinou chybu.

Důležité

Před voláním @@TRANCOUNT v ROLLBACK TRANSACTION bloku vždy zkontrolujte CATCH. Tím se zabrání chybě "Požadavek ROLLBACK TRANSACTION nemá žádnou odpovídající POČÁTEČNÍ TRANSAKCI".

Vyvolání vlastních chyb pomocí THROW

Příkaz THROW vyvolá výjimku s vlastním číslem chyby a zprávou. Slouží k signalizaci chybových podmínek specifických pro aplikaci:

CREATE PROCEDURE ProcessOrder
    @OrderID INT,
    @Quantity INT
AS
BEGIN
    BEGIN TRY
        -- Validate input and raise custom errors for invalid data
        IF @Quantity <= 0
            THROW 50001, 'Quantity must be greater than zero.', 1;

        IF NOT EXISTS (SELECT 1 FROM Orders WHERE OrderID = @OrderID)
            THROW 50002, 'Order not found.', 1;

        -- Process the order
        UPDATE Orders
        SET Quantity = @Quantity
        WHERE OrderID = @OrderID;

    END TRY
    BEGIN CATCH
        -- Log the error before reraising
        EXEC LogError;

        -- THROW without parameters reraises the current error
        THROW;
    END CATCH;
END;

Vlastní čísla chyb pro uživatelem definované chyby musí být 5 0000 nebo vyšší. Parametr stavu (1 v příkladech) je uživatelem definovaná hodnota mezi 1 a 255, která může pomoct identifikovat, kde byla chyba vyvolána.

Používá se pro formátované zprávy.RAISERROR

RAISERROR poskytuje více možností formátování než THROW, včetně nahrazení parametrů ve stylu printf. Zahrnutí hodnot za běhu do chybových zpráv usnadňuje ladění, protože můžete zjistit, která data způsobila selhání, aniž byste museli procházet protokoly nebo reprodukovat problém:

DECLARE @ProductName NVARCHAR(100) = 'Widget Pro';
DECLARE @CurrentStock INT = 5;
DECLARE @RequestedQty INT = 10;

IF @CurrentStock < @RequestedQty
BEGIN
    RAISERROR(
        'Insufficient stock for product "%s". Available: %d, Requested: %d',
        16,  -- Severity
        1,   -- State
        @ProductName,
        @CurrentStock,
        @RequestedQty
    );
END;

Poznámka:

THROW je doporučený postup pro nový kód, protože je jednodušší a vždy obsahuje výpis zásobníku. Použijte RAISERROR , když potřebujete formátované zprávy nebo kompatibilitu se stávajícími vzory zpracování chyb.

Implementace vnořeného zpracování chyb

Uložené procedury, které volají jiné procedury, potřebují koordinované zpracování chyb. Každá úroveň by měla správně zpracovávat vlastní čištění a šířit chyby:

CREATE PROCEDURE OuterProcedure
AS
BEGIN
    BEGIN TRY
        -- Outer procedure owns the transaction
        BEGIN TRANSACTION;

        -- First operation in the outer procedure
        UPDATE SomeTable SET Column1 = 'Value';

        -- Call nested procedure - if it fails, error propagates here
        EXEC InnerProcedure;

        -- All operations succeeded, commit the transaction
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        -- Outer procedure handles rollback for all nested calls
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION;

        -- Propagate error to the application
        THROW;
    END CATCH;
END;
GO

CREATE PROCEDURE InnerProcedure
AS
BEGIN
    BEGIN TRY
        -- Inner procedure does its work within the outer's transaction
        UPDATE AnotherTable SET Column2 = 'Value';
    END TRY
    BEGIN CATCH
        -- Don't rollback here - let the outer procedure handle it
        -- This keeps transaction management in one place
        THROW;  -- Re-raise error to outer procedure
    END CATCH;
END;

Použijte XACT_ABORT pro automatické vrácení zpět

Pokud dojde k nějaké chybě, můžete nastavit XACT_ABORT ON tak, aby SQL Server automaticky vrátil transakci zpět, a to i bez TRY...CATCH takového:

SET XACT_ABORT ON;

BEGIN TRANSACTION;
    UPDATE Table1 SET Col1 = 'A';
    UPDATE Table2 SET Col2 = 'B';  -- If this fails, entire transaction rolls back
    UPDATE Table3 SET Col3 = 'C';
COMMIT TRANSACTION;

Kombinace XACT_ABORT a TRY...CATCH poskytuje výhody obou přístupů: XACT_ABORT zaručuje okamžité vrácení zpět pro všechny chyby, přičemž TRY...CATCH umožňuje zaznamenávat podrobnosti o chybách a provádět vlastní vyčištění před šířením chyby.

-- XACT_ABORT ON ensures automatic rollback on any error
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;

    -- Execute multiple procedures as a single unit of work
    EXEC Procedure1;  -- If any of these fail...
    EXEC Procedure2;  -- ...XACT_ABORT automatically rolls back...
    EXEC Procedure3;  -- ...and jumps to the CATCH block

    -- All succeeded, commit the changes
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- With XACT_ABORT ON, the transaction is usually already rolled back
    -- This check handles edge cases where it might still be active
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

    -- Log the error details before re-raising
    EXEC LogError;

    -- Let the caller know an error occurred
    THROW;
END CATCH;

Návod

Použití SET XACT_ABORT ON je osvědčeným postupem pro uložené procedury, zejména pro ty, které pokrývají více operací. Zajišťuje konzistentní chování bez ohledu na konkrétní chybu, ke které dochází.

Další informace o zpracování chyb naleznete v tématu TRY... CATCH (Transact-SQL) a THROW (Transact-SQL).