TRY...CATCH ile hataları yönetin

Tamamlandı

Üretim veritabanı uygulamaları beklenmeyen durumları düzgün bir şekilde işlemelidir. Sıfıra bölme, kısıtlama ihlalleri, bağlantı zaman aşımları ve geçersiz veriler hatalara neden olabilir. İşlenmeyen hatalar belirsiz hata iletilerine, tamamlanmamış işlemlere veya uygulama kilitlenmelerine neden olur. Doğru hata işleme, T-SQL kodunuzun tahmin edilebilir şekilde başarısız olmasını sağlar ve anlamlı geri bildirim sağlar.

Veritabanı işlemleri birden çok kullanıcı, dış sistem ve öngörülemeyen veri girişleri ile aynı anda etkileşim kurar. Yeniden deneyerek başarısız olan bir işlemden kurtarabilecek uygulama kodundan farklı olarak, veritabanı hataları verileri tutarsız bir durumda bırakabilir, bazı satırlar eklenmiş ve diğerleri eklenmemiş veya kilitler süresiz olarak tutulabilir. Hata işleme, bu kaotik hata modlarını kontrollü, öngörülebilir yanıtlara dönüştürür.

İyi tasarlanmış hata işleme, kodunuzu çeşitli yollarla geliştirir:

  • Veri bütünlüğü koruması: Bir işlem yarıda başarısız olduğunda, doğru hata işleme tüm değişikliklerin birlikte işlenmesini veya hiçbirinin kalıcı olmamasını sağlar. Bu olmadan, çok adımlı bir işlem veritabanınızı yalnız bırakılmış kayıtlar, eşleşmeyen toplamlar veya bozuk ilişkilerle bırakabilir.

  • Hata ayıklama verimliliği: Satır numarası, yordam adı ve belirli bir hata iletisi gibi hata ayrıntılarını yakalamak, sorun gidermeyi hızlandırır. Günlüklerde belirsiz hatalar aramak yerine tam olarak nerede ve neden hata oluştuğuna karar veresiniz.

  • Kullanıcı deneyimi: Uygulamalar, şifreli veritabanı hataları yerine "Ürün kimliği yok" gibi anlamlı iletiler görüntüleyebilir. Bu, kullanıcıların neyin yanlış gittiğini ve nasıl düzeltileceğini anlamasına yardımcı olur.

  • İşletimsel görünürlük: Hataların ayrılmış bir tabloda günlüğe kaydedilmesi, performans sorunları öneren bir hata veya zaman aşımı hatalarına işaret eden yinelenen kısıtlama ihlalleri gibi desenleri tanımlamaya yardımcı olan bir denetim izi oluşturur.

  • Zarif bozulma: Bir işlem başarısız olduğunda, hata işleme tüm toplu işin veya saklı yordamın çökmesine neden olmak yerine kodunuzun devam etmesini veya alternatif bir eylem gerçekleştirmesini sağlar.

T-SQL hata işlemeyi uygulama

T-SQL, diğer programlama dillerindeki özel durum işlemeye benzer şekilde bloklar aracılığıyla TRY...CATCH yapılandırılmış hata işleme sağlar. Bir hata oluştuğunda TRY bloğunda, yürütme hatayı uygun şekilde işleyebileceğiniz CATCH bloğuna aktarılır.

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;

Hata işleme olmadan, aynı kod bir hata iletisiyle sonlanır:

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

Uyarı

TRY...CATCH tüm hataları yakalayamıyor. Derleme hataları (söz dizimi hataları, eksik nesneler) ve bağlantıyı kapatan önem derecesi 20 veya üzeri olan hatalar aynı oturumda yakalanamaz.

Hata bilgilerini alma

blok içinde CATCH SQL Server, oluşan hatayla ilgili ayrıntıları almak için aşağıdaki işlevleri sağlar:

İşlev Description
ERROR_NUMBER() Hata numarasını verir
ERROR_MESSAGE() Hata iletisi metninin tamamını döndürür
ERROR_SEVERITY() Hata önem derecesini verir (0-25)
ERROR_STATE() Hata durumu numarasını verir
ERROR_LINE() Hatanın oluştuğu satır numarasını verir
ERROR_PROCEDURE() Saklı yordamın veya tetikleyicinin adını verir

Aşağıdaki örnek, hata ayrıntılarını yakalamayı ve daha sonra çözümlemek üzere bir tabloya kaydetmeyi gösterir.

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;

Tavsiye

Hataları yeniden kaydetmeden önce her zaman günlüğe kaydeder. THROW veya RAISERROR kullandıktan sonra, hata işlevleri yeniden çağrıldığında NULL döner.

TRY...CATCH ile işlemleri yönet

İşlemler içinde hatalar oluştuğunda, kaydedilmemiş işi açıkça geri almanız gerekir. @@TRANCOUNT işlevi size bir işlemin etkin olup olmadığını bildirir.

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;

Bu @@TRANCOUNT kontrol önemlidir çünkü:

  • Daha önce bir hata oluşabilir BEGIN TRANSACTION
  • Bazı hatalar 'e ulaşmadan önce işlemi otomatik olarak geri yükler.
  • Etkin bir işlem olmadan geri alma girişimi başka bir hataya neden oluyor

Önemli

Her zaman bir CATCH bloğunda ROLLBACK TRANSACTION çağırmadan önce @@TRANCOUNT kontrol edin. Bu, "ROLLBACK TRANSACTION isteğinde karşılık gelen BEGIN TRANSACTION yok" hatasını önler.

ile özel hatalar tetikleyin THROW

THROW ifadesi, özel bir hata numarası ve mesajıyla bir istisna oluşturur. Uygulamaya özgü hata koşullarının sinyallerini vermek için bunu kullanın:

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;

Kullanıcı tanımlı hatalar için özel hata numaraları 50000 veya üzeri olmalıdır. Durum parametresi (örneklerde 1), hatanın nerede oluşturulduğunun belirlenmesine yardımcı olabilecek, 1 ile 255 arasında kullanıcı tanımlı bir değerdir.

Biçimlendirilmiş iletiler için RAISERROR kullanın

RAISERROR printf stili parametre değiştirme dahil olmak üzere değerinden THROWdaha fazla biçimlendirme seçeneği sağlar. Hata iletilerine çalışma zamanı değerlerinin dahil olması hata ayıklamayı kolaylaştırır çünkü günlükleri araştırmadan veya sorunu yeniden oluşturmadan hataya tam olarak hangi verilerin neden olduğunu görebilirsiniz:

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;

Uyarı

THROW daha basit olduğundan ve her zaman bir yığın izlemesi içerdiğinden yeni kod için önerilen yaklaşımdır. Biçimlendirilmiş iletilere veya mevcut hata işleme desenleriyle uyumluluğa ihtiyacınız olduğunda kullanın RAISERROR .

İç içe hata işleme mekanizmasını uygula

Diğer yordamları çağıran saklı yordamlar için eşgüdümlü bir hata işleme gereklidir. Her düzey kendi temizleme işlemlerini yapmalı ve hataları uygun şekilde yaymalıdır:

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;

XACT_ABORT'ü otomatik geri alma için kullan.

SQL Server'ı, TRY...CATCH gibi bir şeye ihtiyaç duymadan, herhangi bir hata meydana geldiğinde işlemi otomatik olarak geri alacak şekilde XACT_ABORT ON ayarlayabilirsiniz.

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;

XACT_ABORT ile TRY...CATCH'i birleştirmek, size her iki yaklaşımın da avantajlarını sağlar: XACT_ABORT herhangi bir hata durumunda anında geri almayı garanti ederken TRY...CATCH, hata ayrıntılarının kaydını tutmanıza ve hata yayılmadan önce özel temizleme işlemlerini gerçekleştirmenize olanak tanır.

-- 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;

Tavsiye

SET XACT_ABORT ON kullanmak, özellikle birden çok işleme yayılan saklı yordamlar için en iyi yöntemdir. Oluşan belirli bir hatadan bağımsız olarak tutarlı bir davranış sağlar.

Hata işleme hakkında daha fazla bilgi için bkz . TRY... CATCH (Transact-SQL) ve THROW (Transact-SQL).