Oracle veritabanlarında veri manipülasyonu ve otomasyon süreçlerini bir üst seviyeye taşımak mı istiyorsunuz? Bu makale, Cursor ve Trigger gibi güçlü PL/SQL araçlarını kullanarak veritabanı yönetimini nasıl daha etkin hale getireceğinizi adım adım açıklıyor.
Veritabanları, modern uygulamaların bel kemiğidir ve bu veritabanlarını etkin bir şekilde yönetmek, performansı optimize etmek ve veri bütünlüğünü sağlamak kritik öneme sahiptir. SQL, verilerle etkileşim kurmak için harika bir araç olsa da, bazen daha karmaşık işlemleri gerçekleştirmek veya belirli olaylara otomatik olarak yanıt vermek için ek yeteneklere ihtiyaç duyarız. İşte tam da bu noktada Oracle PL/SQL’in sunduğu Cursor ve Trigger mekanizmaları devreye girer. Bu araçlar, veritabanı işlemlerini programatik olarak kontrol etme, satır bazında işlem yapma, veri değişikliklerini otomatikleştirme ve denetim kayıtları tutma gibi birçok alanda bize esneklik ve güç sağlar.
Özellikle büyük veri kümeleriyle çalışırken veya belirli iş kurallarının sıkı bir şekilde uygulanması gerektiğinde, standart SQL sorguları yetersiz kalabilir. Örneğin, bir satış işlemi gerçekleştiğinde otomatik olarak stok miktarını güncellemek veya hassas bir tabloya her veri girişi yapıldığında bunu bir denetim tablosuna kaydetmek isteyebilirsiniz. Bu tür senaryolar, ya programatik bir yaklaşım ya da olaya dayalı otomatik yanıtlar gerektirir. Cursor’lar, sorgu sonuç kümeleri üzerinde adım adım gezinerek her bir satırı ayrı ayrı işleme yeteneği sunar. Bu, karmaşık hesaplamalar yapmak, belirli koşullara göre verileri güncellemek veya dış sistemlere veri göndermek gibi işlemler için vazgeçilmezdir. Trigger’lar ise, belirli bir veritabanı olayı (örneğin, bir INSERT, UPDATE veya DELETE işlemi) meydana geldiğinde otomatik olarak tetiklenen PL/SQL bloklarıdır. Böylece, veritabanı seviyesinde iş kurallarını zorlayabilir, veri bütünlüğünü koruyabilir ve denetim izleri oluşturabiliriz. Her iki mekanizma da Oracle veritabanı yöneticileri ve geliştiricileri için günlük operasyonlarda önemli kolaylıklar ve kontrol sağlar. Bu yüzden, bu araçların nasıl çalıştığını ve en iyi şekilde nasıl kullanılacağını anlamak, Oracle uzmanlığınızı derinleştirmek adına hayati bir adımdır.
Cursor Nedir ve SQL Verilerini Nasıl Adım Adım İşleriz?
Veritabanı işlemleri sırasında bir SQL sorgusu çalıştırdığınızda, sonuç olarak sıfır, bir veya birden fazla satır içeren bir küme elde edersiniz. Bu sonuç kümesini satır satır işlemek istediğinizde, yani her bir satır üzerinde belirli operasyonlar yapmak istediğinizde, Cursor’lar devreye girer. Cursor, aslında bir sorgu tarafından döndürülen satır kümesi üzerinde işaretçi görevi gören bir PL/SQL yapısıdır. Oracle, her sorguyu dahili olarak bir Cursor ile işler; bunlara “Implicit Cursor” denir. Ancak, daha karmaşık veya özelleştirilmiş işlemler için kendi “Explicit Cursor”larımızı tanımlayabiliriz. Explicit Cursor’lar, sonuç kümesini daha detaylı kontrol etmemizi ve üzerinde programatik olarak gezinmemizi sağlar. Bir Cursor’ın temel yaşam döngüsü; tanımlama (DECLARE), açma (OPEN), verileri getirme (FETCH) ve kapatma (CLOSE) adımlarından oluşur. Bu adımlar, bize her bir satırı ayrı ayrı ele alma esnekliği sunarak, toplu işlemlerin ötesinde bir kontrol düzeyi sağlar.
Cursor’lar özellikle raporlama, veri temizliği, karmaşık hesaplamalar veya bir tablodaki verileri başka bir tabloya belirli koşullara göre taşımak gibi senaryolarda oldukça kullanışlıdır. Örneğin, bir tablodaki her çalışanın maaşını belirli kriterlere göre artırmanız gerektiğinde veya bir dizi ürüne indirim uygularken her bir ürünün kategorisine göre farklı indirim oranları uygulamanız gerektiğinde Cursor’lar size yol gösterir. Ayrıca, Cursor’lar hata yönetimi açısından da faydalıdır; her bir satır işlenirken olası hataları yakalayıp bunlara özel tepkiler verebilirsiniz. Ancak, Cursor kullanırken dikkatli olmak gerekir; çok sayıda satırı tek tek işlemek, set tabanlı (yani tek bir SQL sorgusuyla tüm küme üzerinde işlem yapmak) yaklaşımlara göre performansı olumsuz etkileyebilir. Bu nedenle, Cursor’ları yalnızca set tabanlı işlemlerin mümkün olmadığı veya çok karmaşık hale geldiği durumlarda kullanmak en iyi uygulamadır. Yine de, PL/SQL programlamasının vazgeçilmez bir parçasıdır ve doğru kullanıldığında veritabanı işlemlerinizde size büyük bir esneklik sunar.
Açık (Explicit) Cursor Kullanımı: Adım Adım Kılavuz
Açık Cursor’lar, PL/SQL kodunuzda belirli bir sorgu için tanımladığınız ve programatik olarak kontrol ettiğiniz Cursor’lardır. Bu tür Cursor’lar, bir sorgunun döndürdüğü satırları tek tek işlememiz gerektiğinde kullanılır. Aşağıdaki adımlar ve örnek, bir satış tablosundaki yüksek değerli ürünlerin stok durumunu kontrol etme senaryosu üzerinden açık Cursor kullanımını detaylandırmaktadır.
Senaryo: Yüksek Değerli Ürünlerin Stok Kontrolü ve Bildirimi
Bir e-ticaret platformunda, belirli bir eşiğin üzerindeki (örneğin 1000 TL üzeri) ürünlerin stok miktarlarını düzenli olarak kontrol etmemiz ve stokları belirli bir seviyenin (örneğin 5 adet) altına düşen ürünler için stok yöneticisine bildirim göndermemiz gerekiyor. Bu senaryoda, tek bir SQL sorgusu ile tüm karmaşık mantığı uygulamak zor olabilir. Cursor, her bir ürünü ayrı ayrı incelememize olanak tanır.
İlk olarak, ürün bilgilerini içeren bir URUNLER tablomuz olduğunu varsayalım:
CREATE TABLE URUNLER (
urun_id NUMBER PRIMARY KEY,
urun_adi VARCHAR2(100),
fiyat NUMBER(10, 2),
stok_miktari NUMBER(5)
);
INSERT INTO URUNLER (urun_id, urun_adi, fiyat, stok_miktari) VALUES (1, 'Dizüstü Bilgisayar', 12000.00, 15);
INSERT INTO URUNLER (urun_id, urun_adi, fiyat, stok_miktari) VALUES (2, 'Akıllı Telefon', 8500.00, 25);
INSERT INTO URUNLER (urun_id, urun_adi, fiyat, stok_miktari) VALUES (3, 'Oyun Konsolu', 7000.00, 8);
INSERT INTO URUNLER (urun_id, urun_adi, fiyat, stok_miktari) VALUES (4, 'Kablosuz Kulaklık', 950.00, 50);
INSERT INTO URUNLER (urun_id, urun_adi, fiyat, stok_miktari) VALUES (5, '4K Monitör', 4500.00, 4); -- Stok düşük
INSERT INTO URUNLER (urun_id, urun_adi, fiyat, stok_miktari) VALUES (6, 'Kamera', 15000.00, 2); -- Stok çok düşük
COMMIT;
Şimdi, bu tablodaki yüksek değerli (fiyat > 1000 TL) ve stok miktarı 5'in altında olan ürünleri bulmak için bir Cursor kullanalım:
DECLARE
-- 1. Cursor'ı Tanımla (DECLARE)
CURSOR c_yuksek_degerli_urunler IS
SELECT urun_adi, fiyat, stok_miktari
FROM URUNLER
WHERE fiyat > 1000
FOR UPDATE OF stok_miktari; -- İsteğe bağlı: Satırları kilitler
v_urun_adi URUNLER.urun_adi%TYPE;
v_fiyat URUNLER.fiyat%TYPE;
v_stok_miktari URUNLER.stok_miktari%TYPE;
BEGIN
-- 2. Cursor'ı Aç (OPEN)
OPEN c_yuksek_degerli_urunler;
LOOP
-- 3. Verileri Getir (FETCH)
FETCH c_yuksek_degerli_urunler INTO v_urun_adi, v_fiyat, v_stok_miktari;
-- Eğer daha fazla satır yoksa döngüyü sonlandır
EXIT WHEN c_yuksek_degerli_urunler%NOTFOUND;
-- Her bir satır için işlem yap
DBMS_OUTPUT.PUT_LINE('Ürün Adı: ' || v_urun_adi || ', Fiyat: ' || v_fiyat || ', Stok: ' || v_stok_miktari);
IF v_stok_miktari < 5 THEN
DBMS_OUTPUT.PUT_LINE(' *** DİKKAT: ' || v_urun_adi || ' için stok miktarı KRİTİK SEVİYEDE! Yalnızca ' || v_stok_miktari || ' adet kaldı.');
-- Burada e-posta gönderme, başka bir tabloya kayıt atma gibi işlemler yapılabilir.
-- Stok miktarını artırmak veya not eklemek gibi bir güncelleme yapabiliriz
-- UPDATE URUNLER SET stok_miktari = stok_miktari + 10 WHERE CURRENT OF c_yuksek_degerli_urunler;
-- DBMS_OUTPUT.PUT_LINE(' Stok miktarı 10 artırıldı.');
END IF;
END LOOP;
-- 4. Cursor'ı Kapat (CLOSE)
CLOSE c_yuksek_degerli_urunler;
COMMIT; -- Eğer UPDATE işlemi yapıldıysa, değişiklikleri kalıcı hale getir
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Bir hata oluştu: ' || SQLERRM);
IF c_yuksek_degerli_urunler%ISOPEN THEN
CLOSE c_yuksek_degerli_urunler;
END IF;
ROLLBACK; -- Hata durumunda yapılan değişiklikleri geri al
END;
/
Bu kod bloğunda, FOR UPDATE OF stok_miktari yan cümlesi, Cursor tarafından seçilen satırları kilitler. Bu, siz o satırı işlerken başka bir işlemin aynı satırı değiştirmesini engeller ve veri tutarlılığını sağlar. Döngü içerisinde c_yuksek_degerli_urunler%NOTFOUND özelliğini kullanarak Cursor'ın tüm satırları işleyip işlemediğini kontrol ediyoruz. Eğer UPDATE URUNLER SET stok_miktari = stok_miktari + 10 WHERE CURRENT OF c_yuksek_degerli_urunler; gibi bir ifade kullanırsak, bu, o an Cursor'ın üzerinde durduğu satırı günceller. Bu, Cursor'ların satır bazında güncelleme yapabilme yeteneğini gösterir.
FOR UPDATE ile satır kilitleme, eşzamanlılık sorunlarını çözmek için önemlidir.
Trigger'lar ile Veritabanı Otomasyonu Nasıl Sağlanır?
Trigger'lar, Oracle veritabanlarında otomasyonun ve veri bütünlüğünün temel taşlarından biridir. Adından da anlaşılacağı gibi, belirli bir olay (event) meydana geldiğinde otomatik olarak "tetiklenen" PL/SQL kod bloklarıdır. Bu olaylar genellikle DML (Data Manipulation Language - INSERT, UPDATE, DELETE) işlemleri, DDL (Data Definition Language - CREATE, ALTER, DROP) işlemleri veya veritabanı sistem olayları (LOGON, LOGOFF, STARTUP, SHUTDOWN) olabilir. Trigger'lar sayesinde, uygulama katmanında veya manuel olarak yapılması gereken birçok işlemi veritabanı seviyesine taşıyabilir, böylece iş kurallarının tutarlı bir şekilde uygulanmasını sağlayabiliriz. Bir Trigger'ın en büyük avantajlarından biri, tetiklendiği olayın bir parçası olarak çalışmasıdır; bu da onun ilgili işlemle aynı transaction (işlem) içinde yer almasını garanti eder. Bu sayede, hem olayın gerçekleşmesi hem de Trigger'ın işleyişi atomik bir bütünlük içinde yönetilir.
Trigger'lar, farklı tetiklenme zamanlarına ve granülerlik seviyelerine sahip olabilir. Örneğin, bir DML işlemi gerçekleşmeden önce (BEFORE) veya sonra (AFTER) tetiklenebilirler. Ayrıca, işlemden etkilenen her bir satır için (FOR EACH ROW) veya tüm ifade için sadece bir kez (FOR EACH STATEMENT) çalışabilirler. Bu çeşitlilik, Trigger'ları denetim (auditing), karmaşık veri doğrulama, otomatik varsayılan değer atama, türetilmiş verileri güncelleme, veri kopyalama veya replikasyon gibi geniş bir kullanım alanı yelpazesinde son derece güçlü kılar. Örneğin, bir kullanıcının maaşını güncellemeden önce eski maaşını bir kayıt tablosuna kaydetmek isteyebilirsiniz (BEFORE UPDATE, FOR EACH ROW). Ya da bir tabloya yeni bir müşteri eklediğinizde, bu müşteriye otomatik olarak bir hoş geldin e-postası gönderilmesi gerektiğini tetikleyici ile yönetebilirsiniz (AFTER INSERT). Bu otomasyon yeteneği, geliştiricilerin uygulama kodunu daha sade tutmasına ve veritabanının kendi içinde güçlü iş mantığı barındırmasına olanak tanır. Ancak, Trigger'ları kullanırken dikkatli olmak gerekir, çünkü aşırıya kaçan veya kötü tasarlanmış Trigger'lar performansı düşürebilir, hata ayıklamayı zorlaştırabilir ve beklenmeyen yan etkilere yol açabilir. Doğru kullanıldığında, Trigger'lar Oracle veritabanlarınızın omurgasını oluşturan güçlü ve güvenilir araçlardır.
Bir Denetim (Audit) Trigger'ı Nasıl Oluşturulur?
Veritabanlarında yapılan değişiklikleri izlemek, güvenlik, uyumluluk ve hata ayıklama açısından son derece önemlidir. Hangi kullanıcının, ne zaman, hangi veriyi ne şekilde değiştirdiğini bilmek, denetim (auditing) kayıtlarının temelini oluşturur. Trigger'lar, bu tür denetim mekanizmalarını otomatik ve veritabanı seviyesinde uygulamak için idealdir. Bir denetim Trigger'ı genellikle AFTER INSERT OR UPDATE OR DELETE ON TABLE_NAME FOR EACH ROW yapısında tanımlanır, çünkü her bir satır değişikliğinin kaydedilmesi hedeflenir.
Senaryo: Çalışan Maaş Değişikliklerinin Kaydedilmesi
Bir insan kaynakları sisteminde, çalışanların maaş bilgilerinin hassas olması nedeniyle, her maaş değişikliğinin (ekleme, güncelleme, silme) kim tarafından, ne zaman ve hangi değerlerle yapıldığının kayıt altına alınması gerekmektedir. Bunun için bir CALISANLAR tablosu ve bir MAAS_DENETIM_LOG tablosu oluşturup, bu iki tablo arasında bir Trigger ile denetim mekanizmasını kuracağız.
Öncelikle, tablolarımızı oluşturalım:
-- CALISANLAR tablosu
CREATE TABLE CALISANLAR (
calisan_id NUMBER PRIMARY KEY,
ad VARCHAR2(50),
soyad VARCHAR2(50),
maas NUMBER(10, 2)
);
-- Denetim log tablosu
CREATE TABLE MAAS_DENETIM_LOG (
log_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
calisan_id NUMBER,
eski_maas NUMBER(10, 2),
yeni_maas NUMBER(10, 2),
islem_turu VARCHAR2(10), -- 'INSERT', 'UPDATE', 'DELETE'
islem_tarihi TIMESTAMP DEFAULT SYSTIMESTAMP,
islem_yapan_kullanici VARCHAR2(100) DEFAULT USER
);
-- Test verisi ekleyelim
INSERT INTO CALISANLAR (calisan_id, ad, soyad, maas) VALUES (101, 'Ayşe', 'Yılmaz', 5000.00);
INSERT INTO CALISANLAR (calisan_id, ad, soyad, maas) VALUES (102, 'Can', 'Demir', 6000.00);
COMMIT;
Şimdi, CALISANLAR tablosundaki maaş değişikliklerini MAAS_DENETIM_LOG tablosuna kaydedecek Trigger'ı oluşturalım:
CREATE OR REPLACE TRIGGER trg_maas_denetimi
AFTER INSERT OR UPDATE OR DELETE ON CALISANLAR
FOR EACH ROW
DECLARE
v_islem_turu MAAS_DENETIM_LOG.islem_turu%TYPE;
BEGIN
IF INSERTING THEN
v_islem_turu := 'INSERT';
INSERT INTO MAAS_DENETIM_LOG (calisan_id, eski_maas, yeni_maas, islem_turu)
VALUES (:NEW.calisan_id, NULL, :NEW.maas, v_islem_turu);
ELSIF UPDATING THEN
v_islem_turu := 'UPDATE';
INSERT INTO MAAS_DENETIM_LOG (calisan_id, eski_maas, yeni_maas, islem_turu)
VALUES (:NEW.calisan_id, :OLD.maas, :NEW.maas, v_islem_turu);
ELSIF DELETING THEN
v_islem_turu := 'DELETE';
INSERT INTO MAAS_DENETIM_LOG (calisan_id, eski_maas, yeni_maas, islem_turu)
VALUES (:OLD.calisan_id, :OLD.maas, NULL, v_islem_turu);
END IF;
END;
/
Bu Trigger'ı inceleyelim:
AFTER INSERT OR UPDATE OR DELETE ON CALISANLAR: Bu Trigger,CALISANLARtablosunda bir ekleme, güncelleme veya silme işlemi sonrasında tetiklenecektir.FOR EACH ROW: İşlemden etkilenen her bir satır için ayrı ayrı çalışır. Bu, denetim kayıtları için idealdir çünkü her satırın eski ve yeni değerlerini yakalamamızı sağlar.:NEWve:OLDPseudo-kayıtları: Bu özel değişkenler, Trigger içindeki satır bazında operasyonlarda çok önemlidir.:NEW: INSERT veya UPDATE işleminden sonraki satırın yeni değerlerini temsil eder.:OLD: UPDATE veya DELETE işleminden önceki satırın eski değerlerini temsil eder.
INSERTING,UPDATING,DELETINGKoşulları: Bu koşullar, Trigger'ın hangi DML işlemi tarafından tetiklendiğini anlamamızı sağlar ve buna göre farklı mantıklar uygulamamıza olanak tanır.
Trigger'ın çalışmasını test edelim:
-- Yeni çalışan ekle
INSERT INTO CALISANLAR (calisan_id, ad, soyad, maas) VALUES (103, 'Elif', 'Kara', 7500.00);
-- Maaşı güncelle
UPDATE CALISANLAR SET maas = 5500.00 WHERE calisan_id = 101;
-- Çalışanı sil
DELETE FROM CALISANLAR WHERE calisan_id = 102;
COMMIT;
-- Denetim kayıtlarını kontrol et
SELECT * FROM MAAS_DENETIM_LOG ORDER BY log_id;
Yukarıdaki sorguyu çalıştırdığınızda, MAAS_DENETIM_LOG tablosunda yapılan tüm değişikliklerin eksiksiz bir şekilde kaydedildiğini göreceksiniz. Bu, Trigger'ın denetim ihtiyaçları için ne kadar güçlü bir araç olduğunu göstermektedir. Bu sayede, kimin ne zaman hangi veriyi değiştirdiğini kolayca takip edebilir, herhangi bir tutarsızlık durumunda geçmişe dönük bilgilere ulaşabiliriz.
AFTER tetikleyiciler olarak oluşturulur. Bunun nedeni, işlemin başarılı bir şekilde tamamlandığından emin olduktan sonra loglama yapmaktır. Eğer işlem başarısız olursa, loglama yapılmasına gerek kalmaz. Ayrıca, :NEW ve :OLD değerlerini kullanırken veri tiplerinin eşleştiğinden emin olun.
Veri Bütünlüğünü Sağlayan Trigger Örnekleri
Veri bütünlüğü, bir veritabanı sisteminin güvenilirliği ve doğruluğu için temel bir gerekliliktir. Oracle, birincil anahtarlar, yabancı anahtarlar, benzersiz kısıtlamalar ve CHECK kısıtlamaları gibi declarative (bildirimsel) bütünlük kısıtlamaları sunsa da, bazen bu kısıtlamaların ötesine geçen karmaşık iş kurallarını uygulamamız gerekebilir. İşte bu noktada Trigger'lar, veri bütünlüğünü programatik olarak zorlamak için son derece esnek ve güçlü bir araç haline gelirler. Trigger'lar sayesinde, verilerin belirli bir formatta olmasını sağlayabilir, tablolar arası karmaşık ilişkileri yönetebilir veya belirli koşullar altında veri değişikliklerini engelleyebiliriz. Bu, uygulamanın farklı yerlerinden gelen veri girişlerinin bile aynı kurallara uymasını garanti eder ve veri tutarsızlıklarını baştan engeller.
Örneğin, bir bankacılık uygulamasında, bir hesabın bakiyesinin asla negatif olmaması gerektiğini garanti etmek isteyebilirsiniz. Standart bir CHECK kısıtlaması bunu yapabilir, ancak bakiye birden fazla işlemle değişiyor ve her işlemin bir kural seti ile doğrulanması gerekiyorsa, Trigger daha uygun bir çözüm olabilir. Veya bir çalışanın terfi etmesi durumunda, yeni pozisyonunun maaş aralığı ile eski maaşının uyumlu olup olmadığını kontrol etmek gibi dinamik kurallar Trigger'lar aracılığıyla uygulanabilir. Trigger'lar, genellikle BEFORE INSERT veya BEFORE UPDATE olaylarında veri doğrulama için kullanılır, çünkü bu, verinin tablolara kaydedilmeden önce kontrol edilmesini ve potansiyel olarak hatalı işlemlerin baştan engellenmesini sağlar. Bu sayede, veritabanına yanlış veya tutarsız veri girilmesi engellenmiş olur, bu da sistemin genel güvenilirliğini artırır. Bu esnek yapı, özellikle dinamik ve karmaşık iş kurallarına sahip uygulamalar için vazgeçilmez bir otomasyon katmanı sunar.
Senaryo: Sipariş Miktarının Stoktan Fazla Olmamasını Sağlama
Bir satış siparişi oluşturulurken, sipariş edilen ürün miktarının mevcut stok miktarını aşmamasını sağlamak kritik bir veri bütünlüğü kuralıdır. Bu kural, envanterin doğru yönetilmesi ve müşterilere yanlış vaatlerde bulunulmaması için hayati öneme sahiptir. Bu senaryo için URUNLER (az önce oluşturduğumuz) ve yeni bir SIPARIS_DETAY tablosu kullanalım.
SIPARIS_DETAY tablosunu oluşturalım:
CREATE TABLE SIPARIS_DETAY (
siparis_detay_id NUMBER PRIMARY KEY,
siparis_id NUMBER,
urun_id NUMBER,
miktar NUMBER(5),
FOREIGN KEY (urun_id) REFERENCES URUNLER(urun_id)
);
Şimdi, SIPARIS_DETAY tablosuna bir kayıt eklenmeden önce çalışacak ve sipariş miktarının stoktan fazla olup olmadığını kontrol edecek bir Trigger oluşturalım:
CREATE OR REPLACE TRIGGER trg_siparis_miktar_kontrol
BEFORE INSERT ON SIPARIS_DETAY
FOR EACH ROW
DECLARE
v_mevcut_stok NUMBER(5);
BEGIN
-- Sipariş edilen ürünün mevcut stok miktarını al
SELECT stok_miktari
INTO v_mevcut_stok
FROM URUNLER
WHERE urun_id = :NEW.urun_id;
-- Eğer sipariş miktarı mevcut stoktan fazlaysa hata fırlat
IF :NEW.miktar > v_mevcut_stok THEN
RAISE_APPLICATION_ERROR(-20001, 'HATA: Sipariş edilen miktar (' || :NEW.miktar ||
') mevcut stoktan (' || v_mevcut_stok ||
') fazla olamaz. Urun ID: ' || :NEW.urun_id);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20002, 'HATA: Sipariş edilen ürün bulunamadı. Urun ID: ' || :NEW.urun_id);
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20003, 'Sipariş miktarı kontrolünde beklenmeyen bir hata oluştu: ' || SQLERRM);
END;
/
Bu Trigger'ın özellikleri:
BEFORE INSERT ON SIPARIS_DETAY: Bu Trigger,SIPARIS_DETAYtablosuna bir satır eklenmeden hemen önce tetiklenecektir. Bu, verinin kaydedilmesini engellemek için ideal bir zamandır.FOR EACH ROW: Her bir yeni sipariş detayı satırı için ayrı ayrı kontrol yapar.RAISE_APPLICATION_ERROR(-20001, ...): Bu fonksiyon, belirli bir hata kodu ve mesajıyla PL/SQL hata fırlatmak için kullanılır. Bu hata, işlemi durdurur ve geri alır. Kullanıcıya açıklayıcı bir hata mesajı iletilir.- Hata Yakalama (EXCEPTION):
NO_DATA_FOUNDveOTHERSile ürün bulunamaması veya başka bir beklenmeyen hata durumları da ele alınmıştır.
Trigger'ı test edelim:
-- Geçerli sipariş (stok yeterli)
INSERT INTO SIPARIS_DETAY (siparis_detay_id, siparis_id, urun_id, miktar) VALUES (1, 100, 1, 10);
COMMIT; -- Bu işlem başarılı olmalı
-- Geçersiz sipariş (stok yetersiz - Urun ID 5 (4K Monitör) için stok 4)
INSERT INTO SIPARIS_DETAY (siparis_detay_id, siparis_id, urun_id, miktar) VALUES (2, 100, 5, 5);
-- Bu satır yukarıdaki Trigger nedeniyle hata verecek ve ROLLBACK olacaktır.
-- Hata mesajı: ORA-20001: HATA: Sipariş edilen miktar (5) mevcut stoktan (4) fazla olamaz. Urun ID: 5
-- Olmayan ürün için sipariş
INSERT INTO SIPARIS_DETAY (siparis_detay_id, siparis_id, urun_id, miktar) VALUES (3, 101, 999, 1);
-- Hata mesajı: ORA-20002: HATA: Sipariş edilen ürün bulunamadı. Urun ID: 999
Gördüğünüz gibi, ikinci INSERT işlemi, Trigger tarafından yakalanır ve stok yetersizliği nedeniyle başarısız olur. Bu, veritabanı seviyesinde veri bütünlüğünün nasıl güçlü bir şekilde korunabileceğine dair mükemmel bir örnektir. Bu tür Trigger'lar, karmaşık iş kurallarının doğru ve tutarlı bir şekilde uygulanmasını garantiler, uygulama katmanındaki hataların veritabanına ulaşmasını engeller ve genel sistem kararlılığını artırır.
Cursor ve Trigger Kullanımında İleri Düzey İpuçları ve En İyi Uygulamalar
Cursor'lar ve Trigger'lar Oracle veritabanı programlamasında güçlü araçlar olsa da, bu araçların yanlış veya aşırı kullanımı performans sorunlarına, karmaşıklığa ve hata ayıklama zorluklarına yol açabilir. Bu nedenle, bunları etkin ve verimli bir şekilde kullanmak için bazı ileri düzey ipuçlarını ve en iyi uygulamaları anlamak kritik öneme sahiptir. Bu bölümde, hem performans optimizasyonuna hem de kod kalitesine odaklanarak bu araçları nasıl daha iyi kullanabileceğinizi ele alacağız. Veritabanı sistemlerinin sürekli olarak yüksek performansla çalışması, özellikle büyük ölçekli ve yoğun işlem gören ortamlarda hayati bir gereksinimdir. Trigger'lar gibi otomatikleşen mekanizmaların veya Cursor'lar gibi satır bazında işlem yapan yapıların bilinçsizce kullanılması, tüm sistem üzerinde domino etkisi yaratabilecek performans darboğazlarına neden olabilir. Dolayısıyla, bu araçları sadece ne işe yaradıklarını bilmek değil, aynı zamanda ne zaman ve nasıl kullanılmaları gerektiğini de derinlemesine kavramak gerekmektedir. İleri düzey uygulamalar genellikle daha karmaşık senaryoları kapsar ve bu senaryolarda doğru araç seçimi, projenin başarısı için belirleyici olabilir. Ayrıca, güvenlik ve sürdürülebilirlik gibi konular da bu araçların tasarımı ve uygulanması sırasında göz önünde bulundurulmalıdır.
Bir diğer önemli nokta ise, Cursor'lar ve Trigger'lar arasında doğru dengeyi kurmaktır. Cursor'lar, belirli veri kümeleri üzerinde detaylı ve programatik kontrol sağlarken, Trigger'lar olay tabanlı otomasyon sunar. Her birinin kendine özgü güçlü yönleri ve sınırlamaları vardır. Örneğin, büyük veri kümeleri üzerinde satır bazında işlem yapılması gereken ancak veritabanı sistem kaynaklarının kısıtlı olduğu durumlarda Cursor'ların performansı dikkatle değerlendirilmelidir. Benzer şekilde, çok sayıda Trigger'ın aynı tablo üzerinde çalışması, bir DML işlemi gerçekleştiğinde beklemeyen bir işlem zincirine yol açabilir, bu da performansı düşürür ve hata ayıklamayı son derece karmaşık hale getirir. Bu nedenle, bu araçları tasarlarken sadece mevcut ihtiyacı karşılamakla kalmayıp, gelecekteki ölçeklenebilirlik, bakım kolaylığı ve olası performans etkilerini de hesaba katmak akıllıca olacaktır. En iyi uygulamaları takip etmek, daha robust (sağlam), daha yönetilebilir ve daha performanslı Oracle çözümleri geliştirmenize yardımcı olacaktır.
Performans Optimizasyonu
- Cursor'lar için Set Tabanlı İşlemleri Tercih Edin: Mümkün olduğunca, tek bir
UPDATE,INSERTveyaDELETEsorgusu ile tüm veri kümesini işleyin. Cursor'lar her satır için Oracle ile PL/SQL arasında bağlam değiştirme (context switching) maliyeti getirir, bu da performansı düşürür. Eğer bir Cursor kullanmak zorundaysanız,FOR UPDATEgibi ifadelerle satırları kilitlerken dikkatli olun, çünkü bu eşzamanlılığı etkileyebilir. FORDöngüsü Cursor'ları Kullanın: Açık Cursor'larınDECLARE,OPEN,FETCH,CLOSEadımlarını manuel olarak yönetmek yerine,FOR ... IN ... LOOPyapısını tercih edin. Bu, PL/SQL'in Cursor yönetimini otomatik olarak yapmasını sağlar ve daha temiz, daha az hataya açık kod yazmanıza yardımcı olur.-- FOR döngüsü Cursor örneği FOR rec IN (SELECT urun_adi, stok_miktari FROM URUNLER WHERE stok_miktari < 10) LOOP DBMS_OUTPUT.PUT_LINE('Düşük Stok: ' || rec.urun_adi || ' - ' || rec.stok_miktari); END LOOP;- Bulk Collect ve Forall Kullanımı: Büyük veri kümelerini işlerken, Cursor'ları
BULK COLLECTile toplu olarak belleğe alabilir veFORALLile toplu DML işlemleri yapabilirsiniz. Bu, bağlam değiştirme sayısını önemli ölçüde azaltarak performansı artırır.DECLARE TYPE t_urun_bilgi IS TABLE OF URUNLER%ROWTYPE INDEX BY PLS_INTEGER; v_urunler t_urun_bilgi; BEGIN SELECT * BULK COLLECT INTO v_urunler FROM URUNLER WHERE stok_miktari < 10; FORALL i IN 1 .. v_urunler.COUNT UPDATE URUNLER SET stok_miktari = stok_miktari + 5 WHERE urun_id = v_urunler(i).urun_id; COMMIT; END; / - Trigger'larda Koşullu Mantık: Trigger'larınızı her zaman koşullu olarak çalıştırmaya çalışın. Örneğin, sadece belirli bir sütun güncellendiğinde tetiklenmesini istiyorsanız
WHEN (OLD.kolon_adi != NEW.kolon_adi)veyaIF UPDATING('kolon_adi')gibi koşullar ekleyin. Bu, gereksiz Trigger tetiklenmelerini ve dolayısıyla performansı etkileyen iş yükünü azaltır.
Hata Yönetimi
Hem Cursor'larda hem de Trigger'larda sağlam hata yönetimi çok önemlidir. Özellikle Trigger'lar, bir veritabanı işleminin bir parçası oldukları için, içlerindeki bir hata tüm işlemi geri alabilir (ROLLBACK) ve uygulama katmanında beklenmeyen davranışlara neden olabilir.
- EXCEPTION Blokları Kullanın: PL/SQL bloklarınızda her zaman
EXCEPTIONbloklarını kullanarak olası hataları yakalayın ve uygun şekilde ele alın. Bu, beklenmedik sistem çöküşlerini engeller. - Açık Cursor'ları Kapatma Garantisi: Açık Cursor kullanıyorsanız,
EXCEPTIONbloğunda bile olsa Cursor'ın kapatıldığından emin olun (IF c_cursor_adi%ISOPEN THEN CLOSE c_cursor_adi; END IF;). Aksi takdirde, açık kalan Cursor'lar kaynak sızıntısına yol açabilir. RAISE_APPLICATION_ERROR: Trigger'larda iş kuralı ihlalleri gibi durumlarda özel hata mesajları döndürmek içinRAISE_APPLICATION_ERRORkullanın. Bu, uygulama katmanının hatayı anlamasına ve kullanıcıya daha anlamlı geri bildirim sağlamasına yardımcı olur.
Gerçek Dünya Senaryosu: Stok Yönetiminde Cursor ve Trigger Entegrasyonu
Modern iş uygulamalarında, veritabanı işlemlerinin otomatikleştirilmesi ve veri tutarlılığının sağlanması hayati öneme sahiptir. Bu bölümde, bir e-ticaret platformunun stok yönetim sisteminde hem Cursor'ların hem de Trigger'ların nasıl entegre edilebileceğine dair kapsamlı bir gerçek dünya senaryosu inceleyeceğiz. Bu senaryo, ürünlerin satış işlemlerine göre stok miktarının otomatik güncellenmesini ve belirli bir stok seviyesinin altına düşen ürünlerin otomatik olarak yeniden sipariş için işaretlenmesini içermektedir. Bu entegrasyon, manuel müdahaleyi azaltarak operasyonel verimliliği artırır ve stok tutarsızlıklarından kaynaklanabilecek müşteri memnuniyetsizliğini önler.
Bu senaryonun karmaşıklığı, sadece bir ürün satıldığında stok miktarını düşürmekle kalmayıp, aynı zamanda belirli eşik değerlere göre ek aksiyonlar alınmasını da gerektirmesidir. Örneğin, bir ürünün stoku kritik seviyenin altına düştüğünde, otomatik olarak tedarikçiye bildirim gönderme veya bir "yeniden sipariş" listesine ekleme gibi işlemler Trigger'lar ve Cursor'lar aracılığıyla kusursuzca yönetilebilir. Bu, özellikle binlerce ürün ve günlük yüzlerce sipariş ile çalışan büyük perakende işletmeleri için büyük bir avantaj sağlar. Veritabanı seviyesinde otomasyon, uygulama katmanının daha sade ve odaklı kalmasına yardımcı olurken, aynı zamanda iş kurallarının istikrarlı bir şekilde uygulanmasını garanti eder. Bu entegrasyon, yalnızca operasyonel faydalar sağlamakla kalmaz, aynı zamanda veri bütünlüğünü güçlendirir ve envanter yönetimini çok daha proaktif hale getirir.
Senaryomuz için iki ana bileşenimiz olacak:
- Trigger: Bir sipariş detayı eklendiğinde (yani bir ürün satıldığında), ilgili ürünün stok miktarını otomatik olarak düşürecek ve eğer stok kritik seviyenin altına düşerse bir bildirim veya log kaydı oluşturacak bir Trigger.
- Cursor: Belirli periyotlarla çalıştırılarak (örneğin gece batch işi), stok miktarı çok düşük olan ürünleri tespit edecek ve bunları "yeniden sipariş" tablosuna ekleyecek bir Cursor.
Tablolarımız (URUNLER ve SIPARIS_DETAY daha önce oluşturulmuştu):
-- Stok kritik seviye uyarısı/yeniden sipariş için yeni tablo
CREATE TABLE YENIDEN_SIPARIS_LISTESI (
liste_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
urun_id NUMBER,
talep_edilen_miktar NUMBER(5),
talep_tarihi TIMESTAMP DEFAULT SYSTIMESTAMP,
durum VARCHAR2(20) DEFAULT 'Beklemede',
FOREIGN KEY (urun_id) REFERENCES URUNLER(urun_id)
);
Adım 1: Satış Olduğunda Stok Düşüren Trigger
Bu Trigger, SIPARIS_DETAY tablosuna her yeni satır eklendiğinde (yani bir ürün satıldığında) tetiklenecek. İlgili ürünün stok miktarını düşürecek ve eğer düşen stok kritik bir seviyenin altındaysa YENIDEN_SIPARIS_LISTESI tablosuna bir kayıt ekleyecektir.
CREATE OR REPLACE TRIGGER trg_stok_guncelle_ve_uyari
AFTER INSERT ON SIPARIS_DETAY
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION; -- Trigger içinde bağımsız işlem başlatmak için
v_critical_stok_limit CONSTANT NUMBER := 5; -- Kritik stok eşiği
v_current_stok NUMBER;
BEGIN
-- Ürünün stok miktarını düşür
UPDATE URUNLER
SET stok_miktari = stok_miktari - :NEW.miktar
WHERE urun_id = :NEW.urun_id
RETURNING stok_miktari INTO v_current_stok; -- Güncel stok miktarını al
-- Eğer stok kritik seviyenin altına düştüyse veya çok düşükse, yeniden sipariş listesine ekle
IF v_current_stok <= v_critical_stok_limit THEN
-- Eğer bu ürün zaten listede ve beklemedeyse tekrar ekleme
BEGIN
INSERT INTO YENIDEN_SIPARIS_LISTESI (urun_id, talep_edilen_miktar, durum)
VALUES (:NEW.urun_id, 10, 'Beklemede'); -- Örnek: Her seferinde 10 adet talep et
COMMIT; -- Autonomous Transaction'ı commit et
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
-- Zaten listede var, do nothing or update quantity if needed
NULL;
WHEN OTHERS THEN
-- Hata durumunda loglama veya bildirim
DBMS_OUTPUT.PUT_LINE('Yeniden sipariş listesine eklerken hata: ' || SQLERRM);
ROLLBACK; -- Autonomous Transaction'ı rollback et
END;
END IF;
-- Ana işlem transaction'ı devam ediyor, burada COMMIT/ROLLBACK yapmıyoruz
-- Autonomous transaction kendi başına yönetilir.
END;
/
Burada PRAGMA AUTONOMOUS_TRANSACTION kullanıldı çünkü bir Trigger'ın içinde, ana DML işlemiyle aynı transaction içinde bir COMMIT veya ROLLBACK yapamazsınız. Yeniden sipariş listesine ekleme işlemi ayrı bir transaction'da gerçekleştiği için bu pragmaya ihtiyaç duyulur. Bu, ana sipariş işlemi başarısız olsa bile loglamanın veya yeniden sipariş kaydının kalıcı olmasını sağlar (veya tam tersi, ana işlem başarılı olsa bile loglama başarısız olursa ana işlemi etkilemez).
Adım 2: Düşük Stoklu Ürünleri İşleyen Cursor (Batch İşlemi)
Bu Cursor, belirli aralıklarla (örneğin, bir Oracle Scheduler işi ile her gece) çalıştırılarak, halihazırda kritik seviyede olan ancak henüz YENIDEN_SIPARIS_LISTESI'ne eklenmemiş ürünleri tespit edecek ve bu listeye ekleyecektir. Bu, Trigger'ın kaçırmış olabileceği veya sistemin genel bir stok kontrolü için ek bir güvenlik katmanı sağlar.
DECLARE
CURSOR c_dusuk_stok_urunler IS
SELECT u.urun_id, u.urun_adi, u.stok_miktari
FROM URUNLER u
LEFT JOIN YENIDEN_SIPARIS_LISTESI ysl ON u.urun_id = ysl.urun_id AND ysl.durum = 'Beklemede'
WHERE u.stok_miktari <= 5 -- Kritik stok eşiği
AND ysl.urun_id IS NULL; -- Henüz yeniden sipariş listesinde beklemede olmayanlar
v_talep_miktar NUMBER := 10; -- Her ürün için talep edilecek standart miktar
BEGIN
DBMS_OUTPUT.PUT_LINE('Düşük stoklu ürünlerin kontrolü başlatıldı.');
FOR r_urun IN c_dusuk_stok_urunler LOOP
DBMS_OUTPUT.PUT_LINE('Ürün ID: ' || r_urun.urun_id || ', Adı: ' || r_urun.urun_adi ||
', Stok: ' || r_urun.stok_miktari || ' - Yeniden sipariş listesine ekleniyor.');
INSERT INTO YENIDEN_SIPARIS_LISTESI (urun_id, talep_edilen_miktar, durum)
VALUES (r_urun.urun_id, v_talep_miktar, 'Beklemede');
END LOOP;
COMMIT; -- Cursor işlemi sonunda değişiklikleri kaydet
DBMS_OUTPUT.PUT_LINE('Düşük stoklu ürünlerin kontrolü tamamlandı.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Batch işleminde hata oluştu: ' || SQLERRM);
ROLLBACK;
END;
/
Senaryo Testi:
Mevcut URUNLER ve SIPARIS_DETAY tablolarımızla test edelim:
-- Urun ID 1 (Dizüstü Bilgisayar) - Stok: 15
-- Urun ID 5 (4K Monitör) - Stok: 4 (Zaten düşük)
-- Urun ID 6 (Kamera) - Stok: 2 (Zaten düşük)
-- 1. Satış yap (Urun ID 1, miktar 10)
INSERT INTO SIPARIS_DETAY (siparis_detay_id, siparis_id, urun_id, miktar) VALUES (10, 1000, 1, 10);
-- Trigger tetiklenir: URUNLER.stok_miktari -> 5.
-- Stok kritik seviye (5) olduğu için YENIDEN_SIPARIS_LISTESI'ne 1 numaralı ürün eklenir.
-- Kontrol:
SELECT * FROM URUNLER WHERE urun_id = 1; -- Stok 5 olmalı
SELECT * FROM YENIDEN_SIPARIS_LISTESI WHERE urun_id = 1; -- Kayıt olmalı
-- 2. Satış yap (Urun ID 1, miktar 1)
INSERT INTO SIPARIS_DETAY (siparis_detay_id, siparis_id, urun_id, miktar) VALUES (11, 1001, 1, 1);
-- Trigger tetiklenir: URUNLER.stok_miktari -> 4.
-- YENIDEN_SIPARIS_LISTESI'ne zaten eklenmiş olduğu için DUP_VAL_ON_INDEX hatası alıp NULL olarak geçiştirir.
-- Kontrol:
SELECT * FROM URUNLER WHERE urun_id = 1; -- Stok 4 olmalı
SELECT * FROM YENIDEN_SIPARIS_LISTESI WHERE urun_id = 1; -- Hala tek kayıt olmalı
-- 3. Cursor batch işini çalıştır
-- Yukarıdaki DECLARE / BEGIN / END bloğunu çalıştırın.
-- Urun ID 5 (stok 4) ve Urun ID 6 (stok 2) zaten düşük olduğu ve YENIDEN_SIPARIS_LISTESI'nde henüz olmadığı için,
-- bu ürünler listeye eklenecektir.
-- Kontrol:
SELECT * FROM YENIDEN_SIPARIS_LISTESI; -- 1, 5 ve 6 numaralı ürünler listede olmalı.
COMMIT;
Bu senaryo, Cursor ve Trigger'ların birleşik gücünü gösterir. Trigger, anlık olaylara tepki vererek stok güncellemelerini ve acil durum uyarılarını yaparken, Cursor daha büyük ölçekli ve periyodik stok kontrolleri için kullanılır. Bu entegrasyon, bir veritabanının sadece veri depolamakla kalmayıp, aynı zamanda karmaşık iş mantığını ve otomasyonu etkin bir şekilde yönetebileceğini kanıtlar.
Sonuç: Oracle Veritabanı Yönetiminde Güçlü Araçlar
Oracle veritabanlarında Cursor'lar ve Trigger'lar, veri üzerinde derinlemesine kontrol sağlayan ve iş süreçlerini otomatikleştiren iki temel PL/SQL mekanizmasıdır. Bu makale boyunca, bu güçlü araçların ne olduklarını, nasıl çalıştıklarını ve gerçek dünya senaryolarında nasıl etkin bir şekilde kullanılabileceklerini detaylıca inceledik. Cursor'lar, özellikle bir sorgu tarafından döndürülen veri kümesini satır satır işleme ihtiyacı duyduğumuzda bize programatik bir kontrol imkanı sunar. Böylece, her bir veri satırı üzerinde karmaşık hesaplamalar, koşullu güncellemeler veya özel veri aktarım işlemleri gerçekleştirebiliriz. Diğer yandan, Trigger'lar, veritabanında belirli bir olayın (INSERT, UPDATE, DELETE gibi DML işlemleri veya DDL/Sistem olayları) meydana gelmesi durumunda otomatik olarak tetiklenen kod bloklarıdır. Bu otomasyon yeteneği, veri bütünlüğünü korumak, denetim kayıtları oluşturmak, iş kurallarını veritabanı seviyesinde zorlamak ve çapraz tablo güncellemeleri gibi kritik görevleri verimli bir şekilde yerine getirmemizi sağlar. Her iki aracın da doğru ve bilinçli kullanımı, veritabanı uygulamalarının performansını, güvenilirliğini ve sürdürülebilirliğini önemli ölçüde artırır.
Ancak, bu araçların her zaman en iyi çözüm olmadığını ve yanlış kullanıldığında performans düşüşlerine veya beklenmeyen yan etkilere yol açabileceğini de unutmamak gerekir. Cursor'lar için mümkün olduğunca set tabanlı SQL işlemlerini tercih etmek, Trigger'lar için ise sadece gerçekten ihtiyaç duyulduğunda ve mümkün olduğunca basit tutmak, iyi bir uygulama geliştirme pratiğidir. Özellikle büyük veri kümeleriyle çalışırken, BULK COLLECT ve FORALL gibi ileri düzey PL/SQL özellikleri, Cursor'ların performans maliyetini düşürmeye yardımcı olabilir. Trigger'larda ise koşullu mantık ve otonom transaction'lar gibi yapılar, karmaşık senaryoların üstesinden gelmek için esneklik sunar. Kısacası, Oracle Cursor'ları ve Trigger'ları, veritabanı yöneticileri ve PL/SQL geliştiricileri için vazgeçilmez beceriler sunar. Bu araçlara hakim olmak, sadece daha sağlam ve verimli veritabanı çözümleri oluşturmanıza yardımcı olmakla kalmayacak, aynı zamanda Oracle ekosistemindeki yetkinliğinizi de önemli ölçüde artıracaktır. Unutmayın, her araç gibi, Cursor ve Trigger'ların da ne zaman ve nasıl kullanılacağını bilmek, onları etkin bir şekilde kullanmanın anahtarıdır.
Sıkça Sorulan Sorular (SSS)
1. Cursor'lar veritabanı performansını düşürür mü?
Evet, genellikle düşürür. Cursor'lar her satır için Oracle ile PL/SQL arasında bağlam değiştirme (context switching) maliyeti getirir. Bu, set tabanlı SQL işlemlerine (örneğin tek bir UPDATE veya INSERT INTO ... SELECT sorgusu) kıyasla daha yavaş olabilir. Mümkün olduğunda set tabanlı işlemleri tercih etmek, performans açısından daha verimli bir yaklaşımdır. Ancak, karmaşık satır bazında iş mantığı gerektiren durumlarda veya BULK COLLECT ve FORALL gibi tekniklerle kullanıldığında Cursor'lar optimize edilebilir.
2. Trigger'lar ne zaman kullanılmamalıdır?
Trigger'lar, aşırı kullanıldığında veya kötü tasarımlandığında sorunlara yol açabilir. Trigger'ları kullanmamanız gereken bazı durumlar şunlardır:
- Basit Veri Doğrulama: Bir sütunun boş olmaması veya belirli bir aralıkta olması gibi basit kısıtlamalar için
NOT NULL,CHECKkısıtlamaları veyaFOREIGN KEYgibi bildirimsel bütünlük kısıtlamalarını tercih edin. - Uygulama Katmanı Mantığı: İş mantığının çoğunu uygulama katmanında yönetmek genellikle daha şeffaf ve hata ayıklaması daha kolaydır. Trigger'lar sadece veritabanı seviyesinde zorlanması gereken kurallar için kullanılmalıdır.
- Performans Kritik İşlemler: Çok yoğun DML işlemlerinin olduğu tablolarda karmaşık Trigger'lar, her işlemde ek maliyet yaratarak performansı ciddi şekilde düşürebilir.
- Zincirleme Trigger'lar: Bir Trigger'ın başka bir Trigger'ı tetiklediği ve bu durumun kontrol edilemez bir zincir oluşturduğu senaryolardan kaçınılmalıdır, bu durum hata ayıklamayı zorlaştırır ve beklenmeyen davranışlara neden olabilir.
3. Implicit (örtük) ve Explicit (açık) Cursor arasındaki fark nedir?
İki tür Cursor vardır:
- Implicit Cursor: Oracle, her SQL sorgusu (
SELECT,INSERT,UPDATE,DELETE) için dahili olarak bir Cursor oluşturur ve yönetir. Buna örtük Cursor denir. Örneğin, birSELECT INTOifadesi kullanıldığında Oracle arka planda bir örtük Cursor kullanır. Geliştiricinin bu Cursor üzerinde doğrudan bir kontrolü yoktur ancakSQL%ROWCOUNTveyaSQL%NOTFOUNDgibi niteliklerle sonuçları kontrol edebilir. - Explicit Cursor: Geliştiricinin kendi PL/SQL kodunda
CURSOR ... IS SELECT ...yapısıyla açıkça tanımladığı veOPEN,FETCH,CLOSEgibi komutlarla manuel olarak yönettiği Cursor'lardır. Explicit Cursor'lar, birden fazla satır döndüren sorguların sonuç kümesi üzerinde satır bazında daha detaylı kontrol ve işlem yapmak gerektiğinde kullanılır.
4. Bir Trigger içinde neden PRAGMA AUTONOMOUS_TRANSACTION kullanırız?
Bir Trigger, tetiklendiği ana DML işlemiyle aynı işlem (transaction) içinde çalışır. Bu, Trigger içinde yapılan herhangi bir COMMIT veya ROLLBACK işleminin ana işlemi de etkileyeceği anlamına gelir ki bu genellikle istenmeyen bir durumdur. PRAGMA AUTONOMOUS_TRANSACTION kullanıldığında, Trigger içindeki kod ayrı, bağımsız bir işlem olarak çalışır. Bu sayede, Trigger kendi içinde COMMIT veya ROLLBACK yapabilir ve bu, ana DML işlemini etkilemez. Bu özellik genellikle denetim (auditing) kayıtları gibi, ana işlem başarılı olsa da olmasa da kalıcı olması istenen işlemler için kullanılır.
5. Trigger'larda :NEW ve :OLD ne işe yarar?
:NEW ve :OLD, satır bazında (FOR EACH ROW) tetiklenen Trigger'larda kullanılan sözde (pseudo) kayıtlardır:
:NEW:INSERTveyaUPDATEişleminden sonraki satırın yeni değerlerini temsil eder. BirINSERTTrigger'ında eklenen satırın değerlerini, birUPDATETrigger'ında ise güncellenmiş değerleri içerir.BEFORE INSERT/UPDATETrigger'larında bu değerler değiştirilebilir.:OLD:UPDATEveyaDELETEişleminden önceki satırın eski değerlerini temsil eder. BirUPDATETrigger'ında sütunların güncellenmeden önceki değerlerini, birDELETETrigger'ında ise silinen satırın değerlerini içerir.:OLDdeğerleri Trigger içinde değiştirilemez.
Bu sözde kayıtlar, Trigger'ların veri değişikliklerini analiz etmesi ve buna göre aksiyon alması için hayati öneme sahiptir.
