---
title: Tetikleyiciler
source: 'https://academia.sh/tr/kurslar/ileri-sql/tetikleyiciler'
course: 'İleri SQL'
language: tr
updated: '2026-08-17T18:08:55+00:00'
license: 'CC BY-SA 4.0'
---

# Tetikleyiciler

Tetikleyici tanımı, BEFORE ve AFTER ile satır başına çalışma, denetim kaydı ve türetilmiş durum güncelleme, RAISE ile kural dayatma, görünüm üzerinde INSTEAD OF ve özyineleme riski.

Önceki derste sunucu tarafındaki kod hep çağrıldı: yordam `CALL` ile, işlev sorgu içinde
adıyla. Çağrıldığı yerden okunduğu için ne yaptığı görünürdü.

**Tetikleyici (trigger)** çağrılmaz. Bir tabloya ekleme, güncelleme ya da silme yapıldığında
motor onu kendiliğinden çalıştırır. Bu, güçlü bir araç ve aynı ölçüde bir tuzaktır: sıradan
görünen bir `UPDATE` deyimi, metninde hiç geçmeyen tablolara yazabilir.

## Tanım ve Çalışma Anı

Tetikleyici üç şeyle tanımlanır: hangi tabloyu izlediği, hangi olayda çalıştığı ve olayın
öncesinde mi sonrasında mı çalıştığı.

- `BEFORE`: deyim satırı işlemeden önce çalışır. Değer düzeltmek ya da işlemi reddetmek
  için kullanılır.
- `AFTER`: satır işlendikten sonra çalışır. Başka tablolara yazmak için kullanılır.
- `INSTEAD OF`: satır işlenmez, onun **yerine** tetikleyici çalışır. Görünümler içindir.

`FOR EACH ROW` yazımı, tetikleyicinin etkilenen her satır için ayrı çalıştığını söyler.
Gövde içinde iki özel ad kullanılabilir: `NEW`, satırın yeni hâlini; `OLD`, eski hâlini
verir. Ekleme olayında `OLD`, silme olayında `NEW` tanımsızdır.

İlk kullanım, iki işi birden yapan bir çift tetikleyicidir: türetilmiş durumu güncellemek ve
denetim kaydı yazmak.

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE kitap(id INTEGER PRIMARY KEY, baslik TEXT, rafta INT NOT NULL DEFAULT 1);
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
CREATE TABLE denetim(id INTEGER PRIMARY KEY, olay TEXT, odunc_id INT, ayrinti TEXT);
INSERT INTO kitap VALUES (1,'Kayip Zaman',1),(2,'Sessiz Ev',1);

CREATE TRIGGER odunc_acildi AFTER INSERT ON odunc
FOR EACH ROW
BEGIN
  UPDATE kitap SET rafta = 0 WHERE id = NEW.kitap_id;
  INSERT INTO denetim(olay, odunc_id, ayrinti)
    VALUES ('acildi', NEW.id, 'kitap ' || NEW.kitap_id || ' uye ' || NEW.uye_id);
END;

CREATE TRIGGER odunc_kapandi AFTER UPDATE OF iade ON odunc
FOR EACH ROW WHEN OLD.iade IS NULL AND NEW.iade IS NOT NULL
BEGIN
  UPDATE kitap SET rafta = 1 WHERE id = NEW.kitap_id;
  INSERT INTO denetim(olay, odunc_id, ayrinti)
    VALUES ('kapandi', NEW.id, 'iade ' || NEW.iade);
END;

INSERT INTO odunc(id, kitap_id, uye_id, alis) VALUES (1, 1, 4, '2024-03-11');
UPDATE odunc SET iade = '2024-03-20' WHERE id = 1;

SELECT id, baslik, rafta FROM kitap ORDER BY id;
SELECT id, olay, odunc_id, ayrinti FROM denetim ORDER BY id;
SQL
```

```text
┌────┬─────────────┬───────┐
│ id │   baslik    │ rafta │
├────┼─────────────┼───────┤
│ 1  │ Kayip Zaman │ 1     │
│ 2  │ Sessiz Ev   │ 1     │
└────┴─────────────┴───────┘
┌────┬─────────┬──────────┬─────────────────┐
│ id │  olay   │ odunc_id │     ayrinti     │
├────┼─────────┼──────────┼─────────────────┤
│ 1  │ acildi  │ 1        │ kitap 1 uye 4   │
│ 2  │ kapandi │ 1        │ iade 2024-03-20 │
└────┴─────────┴──────────┴─────────────────┘
```

İki deyim yazıldı: bir `INSERT` ve bir `UPDATE`. Dördü de olan şu: ödünç kaydı açıldı,
kitap raftan düştü, denetim satırı yazıldı; sonra kayıt kapandı, kitap rafa döndü, ikinci
denetim satırı yazıldı. Sonuçtaki `rafta` değerinin 1 olması, ikinci tetikleyicinin
birincinin etkisini geri aldığını gösterir.

`WHEN` yan tümcesi, tetikleyicinin yalnız belirli satırlarda çalışmasını sağlar. Buradaki
koşul, kaydın gerçekten **kapandığı** güncellemeleri ayırıyor; iade tarihinin düzeltildiği
bir güncelleme kitabı ikinci kez rafa koymayacak.

`AFTER UPDATE OF iade` yazımı tetikleyiciyi tek sütuna bağlar. Sütun listesi verilmezse
tetikleyici, tablodaki herhangi bir sütunun güncellenmesinde çalışır — genellikle
istenmeyen bir genişliktir.

## Kural Dayatmak

`BEFORE` tetikleyicileri işlemi reddedebilir. Standart SQL bunun için sinyal deyimi
tanımlar; motorlarda karşılığı bir hata yükseltme işlevidir. Aşağıdaki tetikleyici, rafta
olmayan bir kitabın ödünç verilmesini engeller:

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE kitap(id INTEGER PRIMARY KEY, baslik TEXT, rafta INT NOT NULL DEFAULT 1);
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
INSERT INTO kitap VALUES (1,'Kayip Zaman',0),(2,'Sessiz Ev',1);

CREATE TRIGGER odunc_kontrol BEFORE INSERT ON odunc
FOR EACH ROW
WHEN (SELECT rafta FROM kitap WHERE id = NEW.kitap_id) = 0
BEGIN
  SELECT RAISE(ABORT, 'kitap rafta degil');
END;

INSERT INTO odunc(id, kitap_id, uye_id, alis) VALUES (1, 2, 4, '2024-03-11');
INSERT INTO odunc(id, kitap_id, uye_id, alis) VALUES (2, 1, 7, '2024-03-11');
SELECT id, kitap_id, uye_id FROM odunc ORDER BY id;
SQL
```

```text
Runtime error near line 13: kitap rafta degil (19)
┌────┬──────────┬────────┐
│ id │ kitap_id │ uye_id │
├────┼──────────┼────────┤
│ 1  │ 2        │ 4      │
└────┴──────────┴────────┘
```

Rafta olan kitabın ödüncü geçti, olmayanınki tetikleyicinin verdiği iletiyle reddedildi.
İleti metni tetikleyicide yazılmıştır; uygulamaya ulaşan hata, kısıt ihlali hatalarıyla aynı
yoldan gelir.

Bu araç kısıtların yerini tutmaz. Bir kural bir sütunun tek başına değeriyle ifade
edilebiliyorsa `CHECK` kısıtı, başka bir tablodaki satırın varlığıyla ifade edilebiliyorsa
yabancı anahtar kullanılmalıdır; ikisi de bildirimseldir, planlayıcı tarafından bilinir ve
tetikleyiciden ucuzdur. Tetikleyici, ancak birden çok tabloyu birlikte inceleyen kurallar
için gerekir.

## Görünüm Üzerinde

Karmaşık bir görünüm doğrudan güncellenemez: motor, görünüm satırındaki bir değişikliğin
temel tablolara nasıl yansıyacağını türetemez. `INSTEAD OF` tetikleyicisi bu eşlemeyi elle
tanımlar:

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
INSERT INTO odunc VALUES (1,1,4,'2024-03-01',NULL),(2,3,7,'2024-03-04',NULL);

CREATE VIEW acik_odunc AS SELECT id, kitap_id, uye_id, alis FROM odunc WHERE iade IS NULL;

CREATE TRIGGER acik_odunc_kapat INSTEAD OF DELETE ON acik_odunc
FOR EACH ROW
BEGIN
  UPDATE odunc SET iade = '2024-03-31' WHERE id = OLD.id;
END;

DELETE FROM acik_odunc WHERE id = 1;
SELECT id, alis, COALESCE(iade,'(acik)') AS iade FROM odunc ORDER BY id;
SQL
```

```text
┌────┬────────────┬────────────┐
│ id │    alis    │    iade    │
├────┼────────────┼────────────┤
│ 1  │ 2024-03-01 │ 2024-03-31 │
│ 2  │ 2024-03-04 │ (acik)     │
└────┴────────────┴────────────┘
```

Yazılan deyim bir silmeydi; olan bir güncellemedir. Satır tabloda duruyor, yalnız iade
tarihi doldu. Görünümün açısından bakınca doğru — satır artık "açık ödünç" değil — ama
deyimin metni bunu söylemiyor. Örtük yan etkinin en açık örneği budur.

## Özyineleme

Bir tetikleyici, kendi izlediği tabloyu güncellerse yeniden çalışabilir. Motorlar bunu ya
baştan engeller ya da bir derinlik sınırıyla durdurur:

```bash
sqlite3 -box -header <<'SQL'
PRAGMA recursive_triggers = ON;
CREATE TABLE kitap(id INTEGER PRIMARY KEY, baslik TEXT, sayac INT NOT NULL DEFAULT 0);
INSERT INTO kitap VALUES (1,'Kayip Zaman',0);

CREATE TRIGGER sayac_artir AFTER UPDATE OF sayac ON kitap
FOR EACH ROW
BEGIN
  UPDATE kitap SET sayac = NEW.sayac + 1 WHERE id = NEW.id;
END;

UPDATE kitap SET sayac = 1 WHERE id = 1;
SELECT id, sayac FROM kitap;
SQL
```

```text
Runtime error near line 11: too many levels of trigger recursion
┌────┬───────┐
│ id │ sayac │
├────┼───────┤
│ 1  │ 0     │
└────┴───────┘
```

Tek bir `UPDATE` yazıldı; tetikleyici kendini çağırdı ve zincir derinlik sınırında kesildi.
Deyim tümüyle geri alındığı için sayaç sıfırda kaldı — atomiklik burada da geçerlidir.

Özyinelemenin açık ya da kapalı olması motora göre değişir; kapalıyken aynı tanım sessizce
tek tur çalışır ve sorun görünmez. İki motor arasında taşınan bir şemanın davranışının
değişebileceği yerlerden biri budur.

## Örtük Yan Etkinin Bedeli

Tetikleyicilerin sağladığı yarar açıktır: kural, onu tetikleyen koddan bağımsız olarak her
zaman uygulanır. Uygulamanın hangi katmanından gelirse gelsin, ödünç kaydı açıldığında
denetim satırı yazılır. Bu, kuralın atlanamamasını güvenceye alır.

Bedeli, deyimin metni ile etkisinin ayrışmasıdır. Bunun somut sonuçları şunlardır:

- **Hata ayıklama.** Beklenmedik bir satır değişikliğinin kaynağı sorgu kütüklerinde
  görünmez; şemadaki tetikleyicilerin okunması gerekir.
- **Sıra belirsizliği.** Aynı olaya bağlı birden çok tetikleyicinin çalışma sırası standartta
  belirlenmemiştir; birbirine bağımlı iki tetikleyici yazmak kırılgandır.
- **Toplu işlemler.** Satır başına çalışan tetikleyici, yüz binlik bir güncellemede yüz bin
  kez çalışır; toplu yükleme yollarının bazılarında ise hiç çalışmayabilir.
- **Görünmez maliyet.** Basit görünen bir deyimin planı, tetikleyici gövdesindeki sorguları
  da içerir.

Ölçü şudur: tetikleyici, **veri bütünlüğünü** koruyan ve atlanmaması gereken kurallar için
uygundur — denetim kaydı, türetilmiş sütunun tutarlılığı, çok tablolu kısıtlar. İş akışı
kararları — bildirim göndermek, ücret hesaplamak, dış sistem çağırmak — uygulama katmanında
kalmalıdır. Yazıldıklarında da kısa tutulmalı ve şemayla birlikte belgelenmelidir.

## Özet

- Tetikleyici çağrılmaz; izlediği tabloda olay gerçekleştiğinde motor tarafından çalıştırılır
  ve `NEW` ile `OLD` üzerinden satırın iki hâline erişir.
- `BEFORE` düzeltme ve reddetme, `AFTER` başka tablolara yazma, `INSTEAD OF` görünüm
  güncellemesi içindir; `WHEN` yan tümcesi çalışmayı daraltır.
- Tek sütunla ifade edilebilen kurallar `CHECK` ya da yabancı anahtarla bildirilmelidir;
  tetikleyici çok tablolu kurallar için gerekir.
- Kendi tablosunu güncelleyen tetikleyici özyineleme üretir; motor bunu derinlik sınırıyla
  keser ve deyimin tamamı geri alınır.
- Örtük yan etki, deyimin metni ile etkisini ayırır; hata ayıklamayı, sıra varsayımlarını ve
  toplu işlem maliyetini etkiler.

## Sonraki Adım

Bu konu boyunca sorgular doğru sonucu vermeye odaklandı: alt sorgu doğru kümeyi seçti,
pencere doğru çerçeveyi gördü, işlem doğru anda kesinleşti. Doğruluk tek ölçüt değildir.
İlişkili alt sorgular dersinde iki yazımın aynı sonucu 200'e karşı 25 satır ziyaretiyle
ürettiği ölçülmüş, farkı yaratanın erişim yolu olduğu görülmüştü; o derste bir dizin
eklendiğinde plan `SCAN` yerine `SEARCH` demişti. Sonraki konu bu noktadan başlar. İlk ders,
dizinin ne olduğunu ve aramanın maliyetini nasıl düşürdüğünü kurar; oradan sorgu planını
okumaya ve aynı sonucu daha az işle veren yeniden yazımlara geçilecektir.
