PL/SQL NEDİR? ORACLE’DA PROCEDURE/FUNCTİON VE EXCEPTİON HANDLİNG
Oracle veritabanında sadece sorgu yazmak çoğu zaman yetmez; iş kuralları, doğrulamalar, performans ve güvenilirlik ihtiyaçları devreye girer. İşte bu noktada PL/SQL, SQL’in yanına “programlama kası” ekleyen, veriyle çalışan uygulamaların omurgasını güçlendiren bir dil olarak öne çıkar.
Bu yazıda “PL/SQL nedir?” sorusunu netleştirip, Oracle’da procedure ve function yapılarının nasıl tasarlandığını, gerçekçi kod örnekleriyle ele alacağız. Ayrıca çoğu projenin kritik parçası olan exception handling (hata yönetimi) yaklaşımını, sürdürülebilir ve yönetilebilir bir şekilde kurmanın yollarını göreceksiniz.
Hedefimiz, sadece sözdizimini anlatmak değil; üretim ortamına uygun, okunabilir, test edilebilir ve performanslı PL/SQL bileşenleri kurgulamak. Eğer daha sistematik bir yol haritası isterseniz Oracle PL/SQL eğitimi sayfası üzerinden kapsamı inceleyebilirsiniz.
PL/SQL nedir ve Oracle ekosisteminde neden kritik?
PL/SQL (Procedural Language/SQL), Oracle’ın SQL üzerine eklediği prosedürel programlama katmanıdır. SQL, veri setleri üzerinde deklaratif işlemler yaparken PL/SQL; değişken, koşul, döngü, modülerlik ve hata yönetimi gibi yazılım geliştirme ihtiyaçlarını karşılar. Bu sayede veritabanına yakın iş kuralları yazabilir, tekrar eden işlemleri merkezi hale getirir ve farklı uygulamaların aynı kuralları tutarlı şekilde kullanmasını sağlarsınız.
Özellikle stored procedure ve function gibi nesneler; raporlama, entegrasyon, batch süreçleri, veri doğrulama, ETL adımları ve yüksek trafikli sistemlerde performans avantajı yaratır. Çünkü işlem mantığı veriye yakın çalışır, ağ trafiği azalır, uygulama katmanında tekrar eden sorgu/iş mantığı parçaları tek yerde toplanır.

SQL ile PL/SQL arasındaki temel fark
SQL, “ne istiyorum?” sorusuna odaklanır; örneğin belirli koşulları sağlayan kayıtları getirir veya günceller. PL/SQL ise “nasıl yapacağım?” sorusunu cevaplar: adımları sıralar, ara değerleri tutar, istisnaları yakalar, gerektiğinde işlem akışını yönlendirir. Bu yüzden PL/SQL blokları çoğu zaman bir iş senaryosu akışına benzer.
PL/SQL blok yapısı: Declare – Begin – Exception – End
PL/SQL’in karakteristiği blok yapısıdır. İsteğe bağlı DECLARE bölümünde değişkenler tanımlanır; BEGIN içinde iş mantığı çalışır; EXCEPTION kısmında beklenen/istenmeyen hatalar yönetilir; END ile blok kapanır. Bu yapı, hem okunabilirliği artırır hem de hata yönetimini kodun doğal bir parçası haline getirir.
PL/SQL’de veri tipleri, değişkenler ve temel kontrol akışı
PL/SQL geliştirirken doğru veri tipi seçimi, hem doğruluk hem performans için önemlidir. Oracle SQL veri tipleriyle uyumlu tipler kullanılır; ayrıca PL/SQL’e özgü tipler, record/collection yapıları, %TYPE ve %ROWTYPE gibi güçlü bağlayıcılar sıkça tercih edilir. Örneğin kolon tipi değişirse PL/SQL tarafında otomatik uyum sağlamak için %TYPE kullanımı iyi bir pratiktir.
Kontrol akışında IF, CASE, LOOP, WHILE, FOR gibi yapılarla senaryoyu adım adım kurgularsınız. Ancak veritabanı tarafında “satır satır” yaklaşımın performans maliyeti olabileceğini unutmamak gerekir; doğru yerde toplu işlemler ve set-bazlı SQL kullanımı, PL/SQL’in verimini ciddi artırır.
%TYPE ve %ROWTYPE ile şema değişikliklerine dayanıklılık
Bir tablo kolonunun tipi zaman içinde değişebilir: sayı hassasiyeti artar, varchar uzunluğu değişir, vb. Değişken tanımını kolona bağlamak, bakım maliyetini azaltır. Şemaya bağlı güçlü tipleme özellikle kurumsal sistemlerde hata riskini düşürür.
DBMS_OUTPUT ile hızlı doğrulama ve debug
Geliştirme sürecinde basit doğrulamalar için DBMS_OUTPUT.PUT_LINE kullanılır. Üretimde kalıcı log yaklaşımı farklı olsa da, geliştirme sırasında akışı görmek için pratik bir araçtır. Yine de büyük döngülerde aşırı çıktı üretmek performansı etkileyebilir; ölçülü kullanmak gerekir.
Oracle’da Procedure yazımı: Parametreler, transaction ve pratik tasarım
Procedure, bir işi yapan ve sonuç döndürmek zorunda olmayan (ama OUT parametreyle döndürebilen) PL/SQL program birimidir. Örneğin sipariş oluşturma, stok düşme, kayıt güncelleme, toplu taşıma gibi işlemler procedure ile tasarlanır. Tasarımda parametre tipleri (IN, OUT, IN OUT), transaction sınırları ve hata yönetimi birlikte düşünülmelidir.
Transaction yönetimi konusunda önemli bir ilke: Commit/rollback kararını süreç sahibine göre konumlandırmak. Bazı ekipler commit’i procedure içine koymayı tercih eder; bazıları bunu uygulama katmanına bırakır. Genel olarak bir procedure “işlem sınırı” (unit of work) tanımı ise commit içeride olabilir; değilse katmanda yönetmek daha esnek olur. Bu noktada ekip standardı ve işin doğası belirleyicidir.
Procedure örneği: Sipariş toplamını güncelleme
Aşağıdaki örnek, bir siparişin satırlarından toplam tutarı hesaplayıp sipariş başlığına yazar ve etkilenen satır sayısını OUT parametre ile döndürür. Ayrıca beklenen hataları anlamlı mesajlarla yönetir.
CREATE OR REPLACE PROCEDURE pr_update_order_total (
p_order_id IN orders.order_id%TYPE,
p_rowcount OUT NUMBER
) IS
v_total orders.total_amount%TYPE;
BEGIN
SELECT NVL(SUM(ol.quantity * ol.unit_price), 0)
INTO v_total
FROM order_lines ol
WHERE ol.order_id = p_order_id;
UPDATE orders o
SET o.total_amount = v_total,
o.updated_at = SYSTIMESTAMP
WHERE o.order_id = p_order_id;
p_rowcount := SQL%ROWCOUNT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_rowcount := 0;
RAISE_APPLICATION_ERROR(-20001, 'Sipariş bulunamadı: ' || p_order_id);
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20002, 'Toplam güncellenemedi (order_id=' || p_order_id || '): ' || SQLERRM);
END pr_update_order_total;
/
Burada iki önemli nokta var: (1) SQL%ROWCOUNT ile etkilenen satır sayısını almak, çağıran tarafa net sinyal verir. (2) RAISE_APPLICATION_ERROR ile anlamlı hata kodu/mesajı üretmek, üretim sorunlarını hızla teşhis etmeyi kolaylaştırır.
Function yazımı: Deterministik hesaplar, dönüş tipi ve kullanım senaryoları
Function, değer döndüren program birimidir ve çoğu zaman hesaplama, dönüşüm, validasyon gibi işlemler için idealdir. SQL içinde çağrılabilmesi (belirli kısıtlar dahilinde) onu güçlü kılar; ancak SQL içinden çağrılan function’larda performans ve yan etki (DML/transaction) konuları dikkatle ele alınmalıdır.
İyi tasarlanmış bir function, tekrar eden iş kurallarını tek yerde toplar. Örneğin KDV hesaplama, risk skoru üretme, isim formatlama, tarih aralığı kontrolü gibi kurallar uygulamalar arasında paylaşılıyorsa function seçimi mantıklıdır.
Function örneği: KDV dahil tutar hesaplama
Aşağıdaki örnek, KDV oranını parametre olarak alır ve KDV dahil tutarı döndürür. Hesaplama için net bir dönüş tipi belirlemek ve parametreleri şemaya bağlamak iyi bir pratiktir.
CREATE OR REPLACE FUNCTION fn_amount_with_vat (
p_amount IN NUMBER,
p_vat_rate IN NUMBER DEFAULT 0.20
) RETURN NUMBER IS
v_result NUMBER;
BEGIN
IF p_amount IS NULL THEN
RETURN NULL;
END IF;
v_result := ROUND(p_amount * (1 + p_vat_rate), 2);
RETURN v_result;
EXCEPTION
WHEN VALUE_ERROR THEN
RAISE_APPLICATION_ERROR(-20011, 'Geçersiz sayı formatı: amount=' || p_amount || ', rate=' || p_vat_rate);
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20012, 'KDV hesaplanamadı: ' || SQLERRM);
END fn_amount_with_vat;
/
Function’ı SQL içinde kullanmak isterseniz: SELECT fn_amount_with_vat(total_amount, 0.20) FROM orders; gibi çağırabilirsiniz. Ancak çok büyük setlerde satır bazlı çağrıların maliyetini göz önünde bulundurmak gerekir; gerekirse set-bazlı SQL veya materyalize yaklaşımlar değerlendirilebilir.
Exception Handling: Hata yönetimini sürdürülebilir kılmak
PL/SQL’de hata yönetimi “sonradan eklenen bir yama” değil, tasarımın parçası olmalıdır. Üretim sistemlerinde hata yaşanması kaçınılmazdır: veri tutarsızlığı, kilitlenmeler, ağ kopmaları, izin problemleri, beklenmeyen NULL değerler… Exception handling sayesinde bu durumlarda kontrollü davranabilir, kullanıcıya anlamlı mesaj verebilir ve gerektiğinde güvenli şekilde geri alabilirsiniz.
Oracle hata kodları çoğu zaman teknik seviyededir. Uygulama ekipleri için anlaşılır bir sözleşme oluşturmak adına, alan bazlı hata kodları ve tutarlı mesaj formatları tercih edilir. Bu yaklaşım, izleme/alert süreçlerini de kolaylaştırır.
Ön tanımlı istisnalar ve OTHERS tuzağı
NO_DATA_FOUND, TOO_MANY_ROWS, VALUE_ERROR gibi ön tanımlı istisnalar, sık görülen durumlara özel yakalama sağlar. WHEN OTHERS ise tüm hataları yakalar; bu nedenle “her şeyi yutmak” için değil, loglayıp anlamlı şekilde yükseltmek için kullanılmalıdır. Aksi halde hata kaybolur, kök neden bulunamaz.
İş kuralı hataları için RAISE_APPLICATION_ERROR
İş kuralı ihlallerinde veritabanının teknik hatalarına yaslanmak yerine, uygulama sözleşmesine uygun bir kod/mesaj üretmek daha sağlıklıdır. Örneğin stok yetersizliği bir “constraint” hatasına dönüşmek zorunda değildir; iş kuralları için özel hata kodları tanımlanabilir. Bu sayede çağıran sistemler hatayı sınıflandırabilir.

Cursor kullanımı: Satır satır işlem mi, set-bazlı yaklaşım mı?
Cursor, sorgu sonucunu satır satır işlemek için kullanılır. PL/SQL’de implicit cursor (SQL%ROWCOUNT gibi) ve explicit cursor (CURSOR tanımı, OPEN/FETCH/CLOSE) seçenekleri vardır. Ayrıca FOR cursor loop, en temiz ve güvenli kullanım biçimlerinden biridir; açık-kapatma yükünü azaltır.
Yine de cursor’la satır satır DML yapmak, büyük hacimlerde performans sorunlarına yol açabilir. Bu noktada set-bazlı SQL, toplu DML veya toplu getirme teknikleri (ör. BULK COLLECT, FORALL) değerlendirilmelidir. En iyi yaklaşım, senaryoya göre seçim yapmaktır: küçük hacimlerde sadelik; büyük hacimlerde toplu işleme.
FOR cursor loop ile okunabilir veri işleme
FOR cursor loop, kayıtları record olarak döndürür ve kodu sadeleştirir. Ayrıca yanlışlıkla cursor’u açık bırakma gibi hataları önler. Kural olarak, veri üzerinde küçük dönüşümler/hesaplar yapılacaksa mantıklıdır; çok büyük tablolar için ise toplu işlemler daha etkilidir.
Package, trigger ve modüler tasarım: Kod organizasyonu
Proje büyüdükçe procedure/function sayısı artar; isimlendirme, erişim yetkileri, sürümleme ve bağımlılıklar karmaşıklaşabilir. Bu noktada package yapısı, ilgili fonksiyonları/prosedürleri mantıksal bir çatı altında toplar. Package spec ile dışa açık arayüz, body ile iç implementasyon ayrılır. Böylece hem kapsülleme artar hem de ekip içi sözleşme netleşir.
Trigger’lar ise tablo olaylarına bağlı otomatik çalışan kod parçalarıdır. Ancak trigger, görünmez yan etki üretebildiği için dikkatli kullanılmalıdır. Örneğin basit audit alanlarını doldurmak için uygun olabilir; kompleks iş akışını trigger’a gömmek ise debug ve bakım maliyetini artırabilir. Kurumsal yapılarda trigger kullanımı genellikle standartlarla sınırlandırılır.
Package ile API yaklaşımı
Bir modülün dışa sunduğu işlemleri package spec içinde “API” gibi tanımlamak, kodun yönetimini kolaylaştırır. Uygulama ekipleri yalnızca spec’i referans alır; body değişse bile dış sözleşme sabit kalır. Bu, özellikle CI/CD süreçlerinde güven verir.
Performans ipuçları: Bulk işlemler, doğru indeks kullanımı ve ölçüm
PL/SQL performansı, çoğu zaman SQL performansıyla iç içedir. En iyi PL/SQL kodu, gereksiz döngüleri azaltır ve mümkün olduğunca set-bazlı SQL kullanır. Büyük hacimli işlemlerde “satır satır” DML yerine toplu teknikler öne çıkar. Ayrıca doğru indeks stratejisi, select/update planlarını doğrudan etkiler; bu nedenle PL/SQL tarafında yazdığınız sorguların planlarını incelemek kritik bir adımdır.
Performansı iyileştirmek için önce ölçmek gerekir. Rastgele optimizasyon yerine, AWR/ASH raporları, explain plan ve uygulama metrikleriyle darboğazı tespit edip hedefli iyileştirme yapmak daha sağlıklıdır. Ayrıca exception’ların “normal akış” gibi kullanılması (ör. kontrol için exception’a dayanmak) gereksiz maliyet ve karmaşa üretir.
Toplu işleme için temel prensipler
- Set-bazlı düşünün: Mümkünse tek SQL ile işi bitirin; döngüleri minimize edin.
- Toplu taşıma: Büyük listeleri işliyorsanız toplu getirme ve toplu DML yaklaşımını değerlendirin.
- Yan etkileri azaltın: Gereksiz commit/rollback ve fazla log üretimi performansı düşürür.
- İzlenebilirlik: Hata kodlarını standartlaştırın; üretimde teşhisi kolaylaştırın.
Gerçek hayatta PL/SQL geliştirme akışı: Standartlar ve test yaklaşımı
Kurumsal projelerde PL/SQL kodu, yalnızca çalışıyor olmakla değerlendirilmez; okunabilirlik, isimlendirme, hata sözleşmesi, yetkilendirme modeli ve deploy stratejisi de kritik hale gelir. Örneğin her procedure için tutarlı parametre isimleri, versiyonlama notları, beklenen exception listesi ve minimal bağımlılık yaklaşımı ekibin hızını artırır.
Test tarafında birim test mantığını PL/SQL’e uyarlamak mümkündür: örnek verilerle procedure/function çıktısını doğrulamak, beklenen exception’ları yakalayıp kontrol etmek, “mutlu yol” kadar “kötü yol”u da kapsamak iyi bir pratiktir. Böylece production hataları azalır ve değişiklikler daha güvenle yapılır.
İsimlendirme ve sözleşme önerisi
Örnek bir yaklaşım: procedure’larda “pr_”, function’larda “fn_” öneki, paketlerde modül adı ve tutarlı hata kod aralığı (örn. -20000 ile -20999) kullanmak. Bu tür standartlar, yeni katılan ekip üyelerinin koda hızla adapte olmasını sağlar ve operasyonel destek süreçlerini kolaylaştırır.
Özet: PL/SQL ile sağlam procedure/function ve güvenilir hata yönetimi
PL/SQL; Oracle ortamında veriyle birlikte yaşayan iş mantığını yönetmek için güçlü bir araçtır. Doğru kurgulandığında procedure ve function’lar, tekrar eden işleri merkezileştirir; exception handling ise hataları kontrollü şekilde yöneterek üretimde güvenilirliği artırır. Cursor, package ve performans prensipleriyle birlikte düşünüldüğünde, PL/SQL mimarisi uzun vadede bakım maliyetini düşürür.
Bir sonraki adım olarak, kendi projenizde en sık yapılan işlemleri çıkarıp küçük bir “PL/SQL API” taslağı hazırlamayı deneyin. Ardından hata kodlarını standardize edin ve iki-üç kritik senaryoya test verisiyle doğrulama ekleyin. Daha kapsamlı bir rota için Oracle PL/SQL eğitimi içeriği üzerinden modül başlıklarını inceleyebilirsiniz.


