TL;DR
Trigger SQL adalah blok kode yang otomatis dijalanin database tiap kali ada perubahan data di tabel tertentu, misalnya INSERT, UPDATE, atau DELETE. Trigger paling sering dipakai buat audit log, ngisi kolom otomatis, dan jaga aturan bisnis yang gak bisa ditangani constraint biasa. Kelemahannya: trigger jalan diam-diam, jadi bikin bug yang sulit dilacak kalau kebanyakan.
Trigger SQL adalah blok kode yang otomatis dijalanin database tiap kali ada perubahan data di tabel tertentu. Kamu gak manggil dia. Dia yang nunggu dan langsung jalan sendiri.
Contoh paling gampang: tiap kali harga produk diubah, kamu mau harga lamanya kecatet di tabel log. Tanpa trigger, kamu harus nulis kode pencatatan itu di setiap tempat yang ngubah harga. Dengan trigger, kamu tulis sekali di database.
Di bawah ini: sintaks MySQL dan PostgreSQL, beda BEFORE dan AFTER, contoh kasus stok, plus alasan kenapa trigger bisa jadi mimpi buruk kalau kebanyakan.
Trigger adalah aturan yang nempel di tabel: "kalau ada kejadian X, jalanin kode Y". Kejadiannya salah satu dari INSERT, UPDATE, atau DELETE.
Tiga bagian yang harus kamu tentuin waktu bikin trigger:
Di dalam body, kamu punya akses ke dua kata kunci: OLD buat nilai sebelum perubahan, dan NEW buat nilai sesudah. Di trigger INSERT, OLD gak ada. Di trigger DELETE, NEW gak ada.
Kasus paling umum: catat tiap perubahan harga produk ke tabel log.
CREATE TRIGGER trg_log_harga_produk
AFTER UPDATE ON produk
FOR EACH ROW
BEGIN
IF OLD.harga <> NEW.harga THEN
INSERT INTO log_harga (produk_id, harga_lama, harga_baru, diubah_pada)
VALUES (OLD.id, OLD.harga, NEW.harga, NOW());
END IF;
END;
AFTER UPDATE ON produk artinya trigger nyala setelah ada UPDATE di tabel produk. FOR EACH ROW artinya jalan buat tiap baris yang kena, bukan sekali per perintah.
Kondisi IF OLD.harga <> NEW.harga penting. Tanpa itu, log bakal keisi walaupun yang diubah cuma deskripsi produk.
Hasilnya: tiap ada orang ngubah harga (lewat aplikasi, lewat admin panel, lewat query manual), log-nya kecatet. Gak ada jalan lolos.
PostgreSQL beda: kodenya ditaruh di function terpisah, terus trigger-nya nunjuk ke function itu.
CREATE OR REPLACE FUNCTION log_harga_produk()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.harga IS DISTINCT FROM NEW.harga THEN
INSERT INTO log_harga (produk_id, harga_lama, harga_baru, diubah_pada)
VALUES (OLD.id, OLD.harga, NEW.harga, NOW());
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_log_harga_produk
AFTER UPDATE ON produk
FOR EACH ROW
EXECUTE FUNCTION log_harga_produk();
Dua hal yang beda dari MySQL. Pertama, IS DISTINCT FROM dipakai gantinya <>, soalnya operator ini aman kalau nilainya NULL. Kedua, function-nya wajib RETURN NEW.
Kelebihan cara PostgreSQL: satu function bisa dipakai banyak trigger di banyak tabel. Detail sintaksnya ada di dokumentasi resmi PostgreSQL.
| Aspek | BEFORE | AFTER |
|---|---|---|
| Kapan jalan | Sebelum baris disimpan | Setelah baris disimpan |
| Bisa ubah NEW? | Bisa | Gak bisa |
| Bisa batalin operasi? | Bisa | Gak (barisnya udah masuk) |
| Cocok buat | Validasi, normalisasi nilai | Audit log, update tabel lain |
Contoh BEFORE yang berguna, rapikan input sebelum masuk:
CREATE TRIGGER trg_rapikan_kota
BEFORE INSERT ON pelanggan
FOR EACH ROW
BEGIN
SET NEW.kota = TRIM(NEW.kota);
END;
Spasi di depan-belakang otomatis kebuang. "Bandung " dan "Bandung" gak lagi jadi dua nilai berbeda. Cara lain buat masalah ini bisa kamu baca di fungsi TRIM.
Dataset toko_berkah punya ngulikdata punya dua tabel: produk (dengan kolom stok) dan penjualan.
Masalahnya, stok diupdate manual tiap sore. Waktu aku cocokin stok sistem sama stok fisik di 68 produk, ada 19 produk yang selisih, sekitar 28%. Penyebabnya sederhana: ada transaksi yang lupa dikurangin stoknya.
Trigger nutup lubang itu:
CREATE TRIGGER trg_kurangi_stok
AFTER INSERT ON penjualan
FOR EACH ROW
BEGIN
UPDATE produk
SET stok = stok - NEW.qty
WHERE id = NEW.produk_id;
END;
Sekarang tiap baris penjualan yang masuk langsung motong stok. Gak peduli transaksinya diinput lewat kasir, lewat admin, atau lewat import CSV.
Setelah trigger dipasang, selisih stok di siklus berikutnya turun dari 19 produk jadi 2. Dua-duanya karena barang rusak, bukan karena pencatatan.
Catatan jujur: trigger ini belum nanganin retur. Kalau ada tabel retur, kamu perlu trigger kedua yang nambahin stok balik. Trigger gak otomatis paham logika bisnis kamu.
1. Naruh query berat di dalam trigger. Trigger jalan per baris. UPDATE 100.000 baris = trigger jalan 100.000 kali. Kalau di dalamnya ada JOIN ke tabel besar tanpa index, satu perintah bisa nyangkut berpuluh detik.
2. Trigger yang manggil trigger. Trigger A ngubah tabel B, yang punya trigger sendiri, yang ngubah tabel C. Rantai kayak gini bikin kamu gak bisa nebak apa yang terjadi pas ada masalah. Beberapa database bahkan langsung nolak kalau rantainya balik ke tabel asal.
3. Lupa kondisi perubahan. Trigger AFTER UPDATE tanpa cek OLD.harga <> NEW.harga bakal nulis log tiap kali baris disentuh, walaupun harganya sama. Tabel log kamu bakal meledak isinya sampah.
4. Naruh logika aplikasi di trigger. Kirim email, panggil API, hitung diskon rumit, ini bukan tugas database. Trigger gak keliatan di kode aplikasi, jadi developer baru bakal bingung dari mana asal perilakunya.
5. Gak dokumentasiin. Trigger itu kode yang gak keliatan. Minimal, catat di README: tabel mana punya trigger apa, dan kenapa dia ada.
BEFORE trigger jalan sebelum barisnya disimpan, jadi kamu masih bisa ngubah nilai yang mau masuk atau nolak operasinya. AFTER trigger jalan setelah barisnya kesimpan, jadi datanya udah pasti ada. Pakai BEFORE buat validasi dan normalisasi nilai. Pakai AFTER buat pencatatan ke tabel lain, kayak audit log, karena di titik itu data yang dicatat udah dijamin valid.
Bisa, kalau isinya berat. Trigger jalan buat tiap baris yang kena, jadi kalau kamu UPDATE 100.000 baris sekaligus, triggernya jalan 100.000 kali. Kalau di dalamnya ada query lambat, satu perintah UPDATE biasa bisa nunggu berpuluh detik. Aturan aman: isi trigger cuma operasi ringan, dan hindari query ke tabel besar tanpa index.
Kalau logikanya bisa ditaruh di aplikasi atau di scheduled job, taruh di situ. Trigger jalan diam-diam, gak muncul di kode aplikasi, jadi developer baru sering bingung kenapa data berubah sendiri. Hindari juga trigger yang manggil trigger lain, karena rantai kayak gitu susah dilacak pas ada masalah.
Bisa. PostgreSQL ngizinin banyak trigger di event yang sama dan ngejalanin sesuai urutan abjad namanya. MySQL versi 5.7 ke atas juga udah bisa, dan kamu bisa atur urutannya pakai klausa FOLLOWS atau PRECEDES. Tapi makin banyak trigger di satu tabel, makin susah nebak apa yang sebenernya terjadi pas data diubah.
Di MySQL, jalanin SHOW TRIGGERS; atau query ke information_schema.triggers. Di PostgreSQL, pakai perintah \dS di psql atau query ke pg_trigger. Aku saranin cek ini duluan tiap kamu pegang database warisan orang lain, soalnya trigger sering jadi sumber perilaku aneh yang gak keliatan di kode aplikasi.
Kalau kamu analis dan cuma perlu baca data, trigger jarang kepakai. Tapi paham cara kerjanya bikin kamu ngerti kenapa kadang ada kolom yang keisi sendiri di tabel yang kamu query.
Lanjut belajar soal apa yang terjadi saat data berubah di Transaksi SQL: COMMIT, ROLLBACK, dan ACID, atau cek definisi cepat di glosarium SQL.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Analisa per kuartal, hari kerja, atau musim jadi ribet kalau tiap query ngitung ulang atribut tanggal. Tabel kalender nyimpen semua atribut itu sekali, biar tinggal di-JOIN.
Laporan penjualan harian sering bolong di hari tanpa transaksi. Ini cara bikin deret tanggal lengkap di SQL biar tiap hari muncul, walau nilainya nol.
Struktur organisasi tersimpan sebagai kolom id_atasan yang saling nunjuk. Ini cara narik rantai jabatan, hitung total bawahan, dan span of control cuma pakai SQL.