Yazılarımız

Veri Akademi

SQL PROGRAMLAMA NEDİR? STORED PROCEDURE, TRANSACTİON VE HATA YÖNETİMİ

Veritabanı tarafında “iş” yapan kod yazmak, sadece birkaç SELECT sorgusunu birleştirmekten çok daha fazlasıdır. SQL programlama; kuralları, doğrulamaları, tutarlılık garantilerini ve hataya dayanıklılığı veriye en yakın katmanda kurmanıza yardım eder. Doğru tasarlanmış bir yaklaşım, uygulama katmanını sadeleştirir; veriyi korur, performansı dengeler ve bakım maliyetini düşürür.

Bu yazıda SQL programlama kavramını pratik bir çerçeveyle ele alacağız: stored procedure nasıl tasarlanır, transaction yönetimi ne zaman şarttır, hata yakalama ve raporlama nasıl yapılır, hangi noktalarda performans ve güvenlik birlikte düşünülmelidir. Amaç; “çalışan kod” değil, üretim ortamında sürdürülebilir çalışan kod üretmektir.

Örnekler ağırlıklı olarak SQL Server/T-SQL dili üzerinden anlatılsa da, temel prensipler PostgreSQL, MySQL ve Oracle gibi sistemlere de uyarlanabilir: atomiklik, izolasyon, idempotent operasyonlar, tutarlı hata sözleşmeleri ve ölçülebilir performans.

Stored procedure, transaction ve hata yakalama akışını gösteren veritabanı geliştirme süreci şeması

SQL programlama nedir ve neden önemlidir?

SQL programlama, veritabanı üzerinde tekrar edilebilir iş mantığını kod olarak ifade etme yaklaşımıdır. Sorgu yazmanın ötesinde; veri ekleme/güncelleme akışlarını, kuralları, doğrulamaları, hesaplamaları ve bütünlük kontrollerini veritabanı katmanında standartlaştırır. Bu yaklaşımın en belirgin faydası, aynı iş kuralının farklı uygulamalar tarafından farklı yorumlanmasını engellemesidir.

Örneğin “sipariş oluşturma” işlemi; stok kontrolü, fiyat hesaplama, kampanya koşulları ve ödeme durumuna göre farklı kurallar içerebilir. Bu kuralları uygulama katmanında çoğaltmak yerine, merkezi bir veritabanı rutininde toplamak hem tutarlılık hem de izlenebilirlik sağlar. Ayrıca işlem adımlarının tek bir transaction içinde ele alınması, yarım kalan güncellemelerin oluşturacağı veri tutarsızlığını önler.

Veri katmanında iş kuralı: doğru sınır neresi?

Her kuralı veritabanına taşımak doğru değildir. UI doğrulamaları, esnek iş akışları veya hızlı değişen kurallar uygulama katmanında daha uygun olabilir. Buna karşın veri bütünlüğü, finansal kayıtlar, stok, bakiye gibi kritik alanlar; verinin bulunduğu yerde, mümkünse transaction ile korunmalıdır. Bu sınırı belirlerken “hangi kural bozulursa veri geri döndürülemez biçimde zarar görür?” sorusu iyi bir pusuladır.

Primary ve secondary keyword yaklaşımıyla içerik kurgusu

Bu makalenin primary odağı SQL programlamadır. Buna bağlı olarak stored procedure, transaction yönetimi ve hata yönetimi gibi secondary kavramlar; başlıklar ve alt başlıklarda doğal biçimde işlenir. Böylece hem içerik bütünlüğü korunur hem de okur, aradığı kavramlara kolayca erişir.

Stored procedure ile iş mantığını paketlemek

Stored procedure, parametre alabilen ve belirli bir işi yapan, sunucu tarafında saklanan rutinlerdir. En önemli avantajları; tekrar kullanılabilirlik, erişim kontrolünü merkezileştirme ve sık çağrılan sorgularda plan önbelleğinden yararlanabilmedir. Ayrıca uygulama ekipleri için net bir “veritabanı sözleşmesi” oluşturur: hangi parametreleri bekler, hangi çıktıyı üretir, hangi hataları döndürür?

Parametre tasarımı ve sözleşme netliği

İyi bir stored procedure, parametreleriyle konuşur. Anlamlı isimler, uygun veri tipleri ve varsayılan değerler; hem hataları azaltır hem de bakım kolaylığı sağlar. Örneğin tarih filtreleri için DATETIME yerine mümkünse DATE kullanmak, gereksiz zaman bileşeni kaynaklı hataları engeller. Ayrıca çıktı parametreleri veya tek tip result set stratejisiyle çağıran tarafın beklentisini netleştirmek önemlidir.

Plan önbelleği ve parameter sniffing riski

Stored procedure’lar plan önbelleğinden faydalanır; fakat parameter sniffing, bazı veri dağılımlarında beklenmedik yavaşlamalara yol açabilir. Özellikle çok farklı seçiciliğe sahip parametreler (ör. az bulunan müşteri ile çok bulunan müşteri) aynı planı paylaşınca sorun oluşabilir. Bu durumda seçenekler arasında yeniden derleme stratejileri (RECOMPILE), yerel değişken kullanımı veya istatistik/indeks iyileştirmeleri değerlendirilebilir. Buradaki hedef, “her koşulda aynı plan” değil, “ölçümlenebilir ve öngörülebilir performans”tır.

Transaction yönetimi: atomiklik ve tutarlılık

Transaction yönetimi, birden fazla adım içeren işlemlerin ya tamamen gerçekleşmesini ya da hiç gerçekleşmemesini garanti eder. Bu özellikle para transferi, stok düşümü, muhasebe fişi oluşumu gibi kritik süreçlerde vazgeçilmezdir. Bir adım başarısız olduğunda, önceki adımların geri alınması (rollback) ile sistem tutarlı kalır.

ACID’i pratikte doğru okumak

ACID prensipleri; Atomiklik, Tutarlılık, İzolasyon ve Kalıcılık olarak özetlenir. Atomiklik; tüm adımların tek bir bütün gibi ele alınmasıdır. Tutarlılık; kuralların ihlal edilmemesidir (ör. stok eksiye düşmez). İzolasyon; eşzamanlı işlemlerin birbirini etkilememesidir. Kalıcılık ise commit sonrası verinin kaybolmamasıdır. Üretimde sorunların çoğu, bu maddelerden birinin “varsayılan ayarlarla zaten olur” sanılmasından çıkar.

Commit/Rollback stratejisi ve sınırlar

Transaction sınırlarını gereğinden geniş tutmak kilitlenme sürelerini uzatır; gereğinden dar tutmak ise tutarlılık riskini artırır. Genel yaklaşım, transaction’ı sadece kritik yazma adımlarını kapsayacak şekilde kısa tutmaktır. Ayrıca hata durumunda rollback’in mutlaka garanti altına alınması gerekir; bu nedenle transaction blokları çoğu zaman hata yönetimi ile birlikte tasarlanır.

İzolasyon seviyeleri, kilitlenme ve deadlock senaryoları

İzolasyon seviyeleri, aynı anda çalışan işlemlerin birbirinin verisini nasıl gördüğünü belirler. Yanlış seçilmiş izolasyon seviyesi; kirli okuma, tekrarlanamayan okuma veya hayalet kayıt gibi tutarsız okuma problemlerine yol açabilir. Öte yandan gereğinden yüksek izolasyon, performansı düşürebilir.

Yaygın izolasyon seviyeleri ve etkileri

Read Committed çoğu sistemde varsayılan ve makul bir başlangıçtır; ancak raporlama gibi yoğun okuma senaryolarında sürümleme temelli yaklaşımlar (ör. snapshot) kilitlenmeyi azaltabilir. Serializable en katı seviyedir; güçlü tutarlılık sağlar fakat eşzamanlılık maliyeti yüksektir. Bu kararı verirken “iş akışı hataya mı daha hassas, gecikmeye mi?” sorusunu yanıtlamak gerekir.

Deadlock’ları azaltmak için pratik önlemler

  • Tabloları tutarlı bir sırayla güncellemek (farklı prosedürlerde aynı güncelleme sırası).
  • Transaction süresini kısa tutmak; gereksiz SELECT’leri transaction dışına taşımak.
  • Uygun indekslerle satır taramalarını azaltmak; kilit kapsamını küçültmek.
  • Gerekli durumlarda retry stratejisi uygulamak; transient hataları yönetmek.

Deadlock tamamen “yok edilecek” bir şey değil, yönetilecek bir risktir. Önemli olan; olasılığı düşürmek, oluştuğunda görünür kılmak ve yeniden denemeyi kontrollü yapmak.

Hata yönetimi: TRY...CATCH, hata sözleşmesi ve loglama

Hata yönetimi, SQL programlamanın üretim kalitesini belirleyen ana faktörlerden biridir. Hatalar kaçınılmazdır: veri ihlali, kilit zaman aşımı, deadlock, beklenmedik NULL değerler veya dış sistemlerle entegrasyon kaynaklı problemler. Bu yüzden hedef “hata olmasın” değil, hata olduğunda ne olacağını standartlaştırmaktır.

TRY...CATCH ile güvenli rollback ve anlamlı hata iletimi

T-SQL tarafında TRY...CATCH yapısı; hatayı yakalayarak rollback’i garanti altına almanıza ve çağıran tarafa daha anlaşılır bir mesaj döndürmenize yardım eder. Ayrıca teknik detaylar loglanırken, kullanıcıya gösterilecek mesajların kontrollü olması sağlanır. Böylece hem güvenlik hem kullanıcı deneyimi korunur.

CREATE OR ALTER PROCEDURE dbo.usp_CreateOrder
  @CustomerId INT,
  @OrderDate  DATE,
  @OrderId    INT OUTPUT
AS
BEGIN
  SET NOCOUNT ON;

  BEGIN TRY
    BEGIN TRAN;

    -- Basit doğrulama
    IF @CustomerId IS NULL OR @CustomerId <= 0
      THROW 51000, 'Geçersiz müşteri bilgisi.', 1;

    INSERT INTO dbo.Orders(CustomerId, OrderDate, Status)
    VALUES(@CustomerId, @OrderDate, 'Created');

    SET @OrderId = SCOPE_IDENTITY();

    -- Örnek: stok düşümü/kalem ekleme gibi adımlar burada olur

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

    DECLARE @ErrMsg NVARCHAR(2048) = ERROR_MESSAGE();
    DECLARE @ErrNum INT = ERROR_NUMBER();

    -- Uygulama için standart hata: hem numara hem mesaj
    THROW 51001, @ErrMsg, 1;
  END CATCH
END;

Yukarıdaki yaklaşımda iki önemli nokta var: (1) Transaction başlatıldıysa, CATCH içinde rollback mutlaka garanti ediliyor. (2) Dışarıya fırlatılan hata, “prosedür sözleşmesi” için tek bir aralıkta tutuluyor (ör. 51000+). Bu, uygulama tarafında hata sınıflandırmayı kolaylaştırır.

Loglama ve izleme: sorunu üretimde yakalayabilmek

Hata yönetiminde görünürlük kritik olduğu için, loglama stratejisi belirlemek gerekir. Örneğin hata numarası, prosedür adı, parametre özeti, kullanıcı bağlamı ve zaman bilgisi ayrı bir tabloya yazılabilir. Ancak log tablosuna yazma işlemi de transaction kapsamındaysa, rollback ile kaybolabilir. Bu yüzden bazı ekipler loglamayı ayrı bir mekanizma ile, transaction dışına taşır veya dış sistemlere iletir. Buradaki seçim, kurumun gözlemlenebilirlik standartlarına göre yapılmalıdır.

Uçtan uca örnek: stored procedure çağrısı ve kontrollü hata yakalama

SQL programlamada “nasıl yazdım?” kadar “nasıl kullanacağım?” da önemlidir. Prosedürleri çağıran taraf (uygulama, ETL, başka prosedür) çıktıları ve hataları öngörülebilir biçimde ele almalıdır. Aşağıdaki örnek, prosedürün nasıl çağrılacağını ve hatanın nasıl yakalanacağını gösterir.

DECLARE @NewOrderId INT;

BEGIN TRY
  EXEC dbo.usp_CreateOrder
    @CustomerId = 42,
    @OrderDate  = '2026-02-04',
    @OrderId    = @NewOrderId OUTPUT;

  SELECT @NewOrderId AS CreatedOrderId;
END TRY
BEGIN CATCH
  SELECT
    ERROR_NUMBER()  AS ErrorNumber,
    ERROR_MESSAGE() AS ErrorMessage,
    ERROR_LINE()    AS ErrorLine;
END CATCH;

Bu yapı, test ve operasyonel süreçlerde de işe yarar: otomasyonlar hata numarasını yakalayabilir, izleme sistemleriyle ilişkilendirilebilir ve “aynı hata türü tekrar mı ediyor?” sorusuna cevap üretir. Ayrıca hata mesajlarının gereksiz teknik detay içermemesi, güvenlik ve kullanıcı deneyimi açısından önemlidir.

Performans ve güvenlik: aynı anda düşünmek

SQL programlama; performans optimizasyonu ile güvenliği karşı karşıya getirmez, doğru tasarımla ikisini birlikte iyileştirebilir. Örneğin stored procedure kullanımı, tabloya doğrudan erişimi kapatıp sadece prosedür üzerinden işlem yaptırarak yetkilendirmeyi sadeleştirir. Aynı zamanda parametrik kullanım, SQL injection riskini azaltır.

Yetkilendirme ve en az ayrıcalık prensibi

Uygulama kullanıcılarına tablolara doğrudan SELECT/INSERT yetkisi vermek yerine, yalnızca gerekli stored procedure’lara EXECUTE yetkisi tanımlamak iyi bir pratiktir. Böylece veri erişimi, kontrol edilen giriş noktalarından yapılır. Ayrıca prosedür içinde yapılan kontroller (ör. müşteri sahipliği, tenant ayrımı) ile veri sızıntısı riskleri azaltılır.

Performans için ölçüm: tahmin değil kanıt

İndeks, istatistik, sorgu planı ve kilit beklemeleri; performansın gerçek belirleyicileridir. Bir prosedür yavaşladığında “sunucu yoruldu” gibi genellemeler yerine, yürütme planı, IO istatistikleri ve bekleme tipleri üzerinden analiz yapmak gerekir. Bu süreçte hedef; en çok maliyeti oluşturan adımı bulup, küçük ama etkili iyileştirmelerle sistemi dengelemektir.

Bakım, sürümleme ve test: üretime hazır SQL kodu

SQL kodu da yazılım ürünüdür; sürüm kontrolü, kod inceleme, test ve dağıtım pratikleriyle yönetilmelidir. Prosedürlerin ne zaman değiştiği, hangi sürümle geldiği, hangi tablo/kolonlara bağımlı olduğu takip edilemezse, küçük bir şema değişikliği zincirleme sorunlara yol açabilir.

İdempotent script ve güvenli dağıtım

CREATE OR ALTER gibi idempotent yaklaşımlar, dağıtım script’lerinin tekrar çalıştırılmasını güvenli hale getirir. Ayrıca şema değişiklikleri ile prosedür değişiklikleri aynı release paketinde uyumlu olmalıdır. Üretim ortamına çıkmadan önce staging’de gerçekçi veriyle performans ve hata senaryoları test edilmelidir.

Test senaryoları: sadece “mutlu yol” yetmez

Başarılı akış kadar, hata akışları da test edilmelidir: geçersiz parametre, beklenmeyen NULL, deadlock simülasyonu, kısıt ihlali, zaman aşımı. Böylece hata sözleşmeleri doğrulanır ve izleme/loglama mekanizmalarının çalıştığı kanıtlanır. Bu yaklaşım, “olduğunda bakarız” yerine “olduğunda zaten hazırız” seviyesine geçmenizi sağlar.


Sonuç: SQL programlamada sağlam bir temel nasıl kurulur?

SQL programlama, verinin etrafına doğru koruma katmanını örmektir: stored procedure ile iş mantığını paketlemek, transaction yönetimi ile tutarlılığı garanti etmek, hata yönetimi ile üretimde görünür ve yönetilebilir bir sistem kurmak. Bu üçlü doğru çalıştığında; uygulama katmanları daha yalın, operasyon süreçleri daha sakin ve veri daha güvenli hale gelir.

Eğer bu konuları uygulamalı örneklerle daha sistematik öğrenmek isterseniz, SQL Programlama Eğitimi sayfası üzerinden kapsam ve içerik detaylarına göz atabilirsiniz. Doğru prensiplerle ilerlediğinizde, veritabanı kodu “son çare” değil, mimarinin güvenilir bir parçası olur.

 VERİ AKADEMİ