SQL Server'da TRY...CATCH ile Hata Nasıl Yakalanır? THROW ve Hata Fonksiyonları
Beceri rehberi

SQL Server'da TRY...CATCH ile Hata Nasıl Yakalanır? THROW ve Hata Fonksiyonları

Hazırlayan: Mindset Institute EditörlüğüGüncellendi: 6 dk okuma

TRY...CATCH, T-SQL kodundaki hatayı yakalayıp denetimli biçimde ele almanızı sağlar. Hata bilgisini okumayı, transaction'ı güvenle geri almayı ve hatayı yeniden fırlatmayı adım adım görürsünüz.

Bir bakışta

  1. TRY bloğunda hata olursa denetim CATCH'e geçer. CATCH bloğunun içinde hata bilgisini okur, kayıt tutar ve gerekiyorsa işlemi geri alırsınız.
  2. Yakalanan hata uygulamaya kendiliğinden gitmez. Hatayı yukarıya iletmek için CATCH içinde THROW kullanın; aksi hâlde hata sessizce yutulur.
  3. Transaction içinde XACT_STATE ile geri alma kararı verilir. İşlem onaylanamaz durumdaysa yalnızca ROLLBACK mümkündür; bunu denetlemeden COMMIT yazmayın.
  4. TRY...CATCH her hatayı yakalamaz. Sözdizimi hataları, aynı düzeydeki ad çözümleme hataları ve kopan bağlantılar bloğun dışında kalır.

TRY...CATCH'in yapısı

Yapı iki bloktan oluşur. TRY bloğuna çalıştırmak istediğiniz ifadeleri, CATCH bloğuna hata olduğunda yapılacakları yazarsınız. TRY içinde hata çıkarsa denetim, ilgili CATCH bloğunun ilk ifadesine geçer. Hata çıkmazsa TRY bittiğinde CATCH atlanır ve END CATCH'ten sonraki ifadeden devam edilir.

BEGIN TRY SELECT musteri_id / adet FROM siparis; END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS hata_no, ERROR_MESSAGE() AS iletisi; END CATCH;

Microsoft belgesi birkaç kuralı açıkça belirtir. TRY bloğunun hemen ardından ilgili CATCH bloğu gelmelidir; araya başka ifade yazılırsa sözdizimi hatası olur. Yapı birden çok toplu işe (batch) yayılamaz ve IF...ELSE gibi başka bir yapının iki dalına bölünemez. Kullanıcı tanımlı bir fonksiyonun içinde kullanılamaz. İç içe TRY...CATCH yazılabilir; CATCH içindeki bir hata, iç içe yapı yoksa çağırana iletilir.

Bir yordam TRY bloğunda çağrılırsa ve yordamda kendi TRY...CATCH'i yoksa, yordamın içindeki yakalanmamış hata çağıran blokun CATCH kısmına gelir. Yordamın kendi TRY...CATCH'i varsa hata önce orada ele alınır.

Hata bilgisini okumak

CATCH bloğunun içinde hatanın ayrıntısını almak için yerleşik işlevler vardır: ERROR_NUMBER hata numarasını, ERROR_SEVERITY önem derecesini, ERROR_STATE durum numarasını, ERROR_PROCEDURE hatanın oluştuğu yordam ya da tetikleyicinin adını, ERROR_LINE satır numarasını ve ERROR_MESSAGE ileti metnini döndürür. İleti metni, yer tutucuların aldığı değerleri de içerir.

Belgeye göre bu işlevler CATCH bloğunun dışında çağrılırsa NULL döner. Bloğun içinde ise herhangi bir yerde kullanılabilirler; hata bilgisini bir yordama aktarmak da mümkündür.

Önemli bir ayrıntı: CATCH tarafından yakalanan hata çağıran uygulamaya otomatik olarak gönderilmez. Uygulamanın bilgiyi alması isteniyorsa CATCH içinde sonuç kümesi seçmek, bir ileti yazdırmak ya da hatayı yeniden fırlatmak gerekir. Çoğu gerçek iş akışında amaç hatayı kaydetmek ve yeniden fırlatmaktır; yutulan hata, sorunun fark edilmemesine yol açar.

Transaction içinde kullanım: geri alma kararı

Veri değiştiren kodda TRY...CATCH çoğunlukla bir transaction ile birlikte yazılır. Hata olursa değişikliklerin yarım kalmaması için CATCH içinde ROLLBACK çalıştırılır. Ancak transaction hâlâ açık mıdır, yoksa hata onu geçersiz mi kılmıştır? Bu soruya XACT_STATE cevap verir.

SET XACT_ABORT ON;

BEGIN TRY BEGIN TRANSACTION;

UPDATE stok SET adet = adet - @adet WHERE urun_id = @urun_id; INSERT INTO siparis_kalemi (siparis_id, urun_id, adet) VALUES (@siparis_id, @urun_id, @adet);

COMMIT TRANSACTION; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;

THROW; END CATCH;

Belgeye göre TRY bloğunda oluşan bir hata transaction'ı onaylanamaz duruma sokabilir. Böyle bir transaction yalnızca okuma ya da ROLLBACK yapabilir; yazma işlemi ya da COMMIT yapılamaz. XACT_STATE, onaylanamaz durumda -1 döndürür; transaction onaylanabiliyorsa 1, açık transaction yoksa 0 döner. SET XACT_ABORT ON açıkken kısıtlama ihlali gibi hatalar transaction'ı onaylanamaz yapar. Geri almadan önce durumu denetlemek, 'transaction yok' hatasını da önler.

Şekil 1

XACT_STATE, CATCH içinde ne yapılacağını söyler

Geri almadan önce her zaman durumu sorgulayın

CATCH bloğunda XACT_STATE() kaç döndü?
  1. XACT_STATE() = -1: transaction onaylanamazROLLBACKYalnızca okuma ve geri alma mümkündür
  2. XACT_STATE() = 1: transaction onaylanabilirROLLBACK ya da COMMITHata öncesi yapılanları korumak istiyorsanız COMMIT
  3. XACT_STATE() = 0: açık transaction yokGeri alma yapmaROLLBACK burada hata üretir
Kaynak: Kaynak: Microsoft Learn, TRY...CATCHMindset Institute

THROW ve RAISERROR: hatayı kendiniz üretmek

Bazen hatayı veritabanı değil siz tetiklersiniz: iş kuralı bozulmuştur ve işlemi durdurmak istersiniz. THROW bunun için tasarlanmıştır:

IF @adet <= 0 THROW 50000, N'Adet sıfırdan büyük olmalıdır.', 1;

THROW'un hata numarası belgeye göre 50000 ile üst sınırı arasında bir değer olmalıdır; sys.messages tablosuna önceden tanımlanması gerekmez. Üçüncü parametre durum numarasıdır ve aynı iletiyi birden çok yerde kullandığınızda hatanın hangi noktada oluştuğunu ayırt etmenize yarar. THROW'dan önceki ifade noktalı virgülle bitmelidir; aksi hâlde sözdizimi hatası alırsınız. Yeni bir hata fırlatırken önem derecesi sabittir.

Parametresiz THROW yalnızca CATCH bloğunda kullanılır ve yakalanan hatayı özgün ayrıntılarıyla yeniden fırlatır; önceki örnekte olduğu gibi. RAISERROR ise eski yöntemdir: ileti biçimlendirmesini destekler, önem derecesini siz verirsiniz, ama SET XACT_ABORT ayarına uymaz. Yeni kodda THROW genellikle daha basit ve öngörülebilirdir; RAISERROR'u yalnızca printf tarzı biçimlendirmeye ya da sys.messages'taki bir iletiye ihtiyaç duyduğunuzda düşünün.

THROW iletisinde yüzde işareti ayrılmış bir karakterdir; iletide yüzde göstermek için çift yüzde yazılır.

Şekil 2

Hata önce yakalanır, kaydedilir, sonra yukarıya iletilir

Yakalanan hata kendiliğinden uygulamaya ulaşmaz

  1. Hata oluşurTRY içinde bir ifade başarısız olur ya da THROW ile kural ihlali bildirilir.
  2. CATCH yakalarHata işlevleri okunur, gerekirse transaction geri alınır.
  3. Kayıt ve yeniden fırlatmaHata günlüğe yazılır, THROW ile çağırana iletilir.
Kaynak: Sayfa içeriğiMindset Institute

TRY...CATCH'in yakalayamadığı hatalar

Yapı her şeyi yakalamaz ve bu sınırları bilmek, 'neden CATCH'e girmedi?' sorusunu önler. Belgeye göre şunlar yakalanmaz: uyarı ve bilgi düzeyindeki iletiler, bağlantıyı koparan çok yüksek önem dereceli hatalar, bağlantı kopması ve istemcinin iptal isteği gibi dikkat sinyalleri ve bir yöneticinin KILL ile oturumu sonlandırması.

Ayrıca TRY ile aynı düzeyde oluşan derleme hataları (sözdizimi gibi) ve ad çözümleme hataları CATCH'e düşmez. Örneğin TRY içinde var olmayan bir tabloyu sorgularsanız hata CATCH'e değil bir üst düzeye gider. Aynı sorguyu bir yordamın içinde çalıştırıp yordamı TRY içinden çağırırsanız hata bir alt düzeyde oluştuğu için yakalanır. Bu davranış, dinamik SQL'in sp_executesql ile çalıştırılması için de geçerlidir: derleme ya da ad çözümleme hatası alt düzeyde ortaya çıktığından CATCH tarafından ele alınır.

Pratik sonuç: kritik kodu küçük yordamlara ayırmak, hem sınamayı kolaylaştırır hem de hataların daha öngörülebilir biçimde yakalanmasını sağlar. Sözdizimi hatalarını ise sınama ve kod incelemesi yakalar, çalışma zamanı yapıları değil.

Hata kaydı tutmak

Yakalanan hatayı kalıcı bir tabloya yazmak, sorun giderme için çok değerlidir. Küçük bir günlük tablosu ve CATCH bloğunda bir INSERT yeterlidir:

BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;

INSERT INTO hata_kaydi (hata_no, hata_iletisi, hata_satiri, yordam, kaydedilen) VALUES (ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_LINE(), ERROR_PROCEDURE(), SYSDATETIME());

THROW; END CATCH;

Sıra önemlidir: günlük kaydını geri almadan önce yazarsanız, ROLLBACK kaydı da siler. Önce geri alın, sonra kaydedin, en sonda yeniden fırlatın. Kayıt yazma sırasında yeni bir hata oluşabileceği için, kritik sistemlerde bu kısmı iç içe bir TRY...CATCH ile korumak mantıklıdır; belge de CATCH içindeki hataların diğer hatalar gibi ele alındığını söyler.

Kayıt tablosunda hata numarası, ileti, satır, yordam adı ve zaman damgası yeterli başlangıç alanlarıdır. Hangi kullanıcının ya da hangi girdinin hatayı tetiklediğini eklemek isteyebilirsiniz; kişisel veri taşıyan girdileri kaydetmeden önce gizlilik kurallarınızı kontrol edin.

TRY...CATCH'te sık yapılan hatalar

  • CATCH'te hatayı yutmak. Kayıt tutup yeniden fırlatmazsanız uygulama işlemin başarısız olduğunu bilmez ve veri tutarsızlığı fark edilmeden kalır.
  • ROLLBACK'i koşulsuz yazmak. Açık transaction yoksa ROLLBACK hata üretir; XACT_STATE ya da sayaç denetimi ekleyin.
  • Onaylanamaz transaction'a COMMIT yazmak. XACT_STATE eksi bir döndürüyorsa yalnızca geri alınabilir.
  • THROW'dan önceki ifadeyi noktalı virgülsüz bırakmak. Sözdizimi hatası alırsınız.
  • Günlüğe yazmayı geri almadan önce yapmak. Kayıt da geri alınır.
  • TRY...CATCH'i hata ayıklamayı ertelemek için kullanmak. Beklenen hataları (örneğin iş kuralı ihlalini) önce doğrulamayla önleyin, yalnızca beklenmeyenleri yakalayın.
  • Her ifadeyi ayrı TRY bloğuna sarmak. Anlamlı bir iş birimini (bir transaction'ı) tek blokta toplayın.
  • Hatanın yutulup bildirimin atlandığı iş akışlarını denetlemeden bırakmak. Zamanlanmış işlerde hata bildirimi kurulmamışsa kimse sorunu görmez.

Sağlama için kasıtlı hata üretin: sıfıra bölme ya da yinelenen anahtar ekleme ile CATCH bloğunun çalıştığını, geri almanın veriyi eski hâline getirdiğini ve hata kaydının yazıldığını görün. İşlem geri alma temellerini transaction rehberinde, hata yönetimini yordam içinde kullanımını ise saklı yordam rehberinde bulabilirsiniz.

Hata yönetimini Mindset programlarında nerede çalışırsınız?

Hata yönetimi, saklı yordam ve transaction bilgisinin birleştiği noktadır. SQL ile Programlama Eğitimi sorguları ve veri işlemeyi, satır ekleme, güncelleme ve silmeyi temel düzeyde işler; hata yönetimi bu zeminin üzerine gelir. SQL ile Veri Sorgulama (İleri) programı ise görünümler, stored procedure ve tetikleyiciler modülüyle TRY...CATCH'in yazıldığı bağlamı sunar.

Veritabanı İşlem Yönetimi ve İzolasyon Seviyeleri programının işlem temelleri ve ACID ile kilitleme ve eşzamanlılık denetimi modülleri, geri alma kararının arkasındaki kavramları pekiştirir. Veri güvenilirliği sorumluluğu olan geliştirici ve veri mühendisleri için bu programlar birbirini tamamlar; kapsam ayrıntısı program sayfalarındadır.

İlgili Mindset programı

SQL ile Programlama Eğitimi

Sık sorulanlar

Sık sorulan sorular

CATCH bloğu ile hata uygulamaya otomatik iletilir mi?

Hayır. Belgeye göre CATCH tarafından yakalanan hata çağıran uygulamaya döndürülmez. Uygulamanın hatayı görmesini istiyorsanız CATCH içinde THROW ile yeniden fırlatmalı ya da sonuç kümesi, ileti gibi bir mekanizmayla bilgiyi iletmelisiniz. Sadece kayıt tutup bırakırsanız uygulama işlemin başarılı olduğunu varsayabilir.

THROW ile RAISERROR arasındaki fark nedir?

THROW'un hata numarasının sys.messages tablosunda tanımlı olması gerekmez, önem derecesi yeni hatalarda sabittir ve SET XACT_ABORT ayarına uyar. RAISERROR printf biçimlendirmesini destekler, önem derecesini siz verirsiniz ama XACT_ABORT ayarını dikkate almaz. Yeni kodda THROW daha basittir; biçimlendirme gerekirse RAISERROR düşünülür.

Hata yakalandıktan sonra transaction hâlâ açık mı?

Duruma bağlıdır; bu yüzden CATCH içinde XACT_STATE işlevini çağırın. Eksi bir dönerse transaction onaylanamaz ve yalnızca geri alınabilir. Bir dönerse onaylanabilir, hata öncesi yapılanları korumak ya da geri almak sizin kararınızdır. Sıfır dönerse açık transaction yoktur ve ROLLBACK çalıştırmak hata verir.

Neden bazı hatalar CATCH bloğuna düşmüyor?

Aynı düzeyde oluşan sözdizimi ve ad çözümleme hataları, kopan bağlantılar, istemci iptalleri ve oturumu sonlandırma bloğun dışında kalır. Aynı hata bir alt düzeyde, örneğin yordamın ya da sp_executesql'in içinde oluşursa yakalanır. Yani var olmayan tablo hatasını yordama taşıyıp çağırarak yakalayabilirsiniz.

TRY...CATCH kullanıcı tanımlı fonksiyonda kullanılabilir mi?

Hayır. Belge, bu yapının kullanıcı tanımlı fonksiyonlarda kullanılamayacağını belirtir. Saklı yordamlarda ve tetikleyicilerde kullanılabilir. Fonksiyonda hata yönetimi gerekiyorsa mantığı bir yordama taşımayı ya da hatayı çağıran kodda ele almayı düşünün.

SQL TRY...CATCH ile Hata Yönetimi: THROW ve Örnekler