Práce s chybami pomocí TRY...CATCH
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).