Trigger

Menjalankan logika otomatis melalui trigger BEFORE dan AFTER.

Daftar isi

Topik ini menutup modul objek basis data dengan trigger, yaitu fungsi yang dijalankan server secara otomatis ketika peristiwa tertentu terjadi pada tabel. Trigger memindahkan logika yang wajib dijalankan ke tempat yang tidak dapat dilewati jalur penulisan mana pun, dan waktunya dinyatakan melalui BEFORE atau AFTER.

Blok penyiapan dataset#

bash
# siapkan ulang tabel dan data contoh untuk topik ini
PGOPTIONS='-c client_min_messages=warning' psql -q -U postgres -d perpustakaan <<'SQL'
DROP TABLE IF EXISTS peminjaman, buku, anggota, kategori, riwayat_anggota CASCADE;
CREATE TABLE kategori (id SERIAL PRIMARY KEY, nama TEXT NOT NULL UNIQUE);
CREATE TABLE anggota (id SERIAL PRIMARY KEY, nama TEXT NOT NULL, email TEXT UNIQUE, bergabung DATE NOT NULL, aktif BOOLEAN NOT NULL DEFAULT TRUE);
CREATE TABLE buku (id SERIAL PRIMARY KEY, judul TEXT NOT NULL, penulis TEXT NOT NULL, tahun INTEGER NOT NULL, kategori_id INTEGER REFERENCES kategori(id));
CREATE TABLE peminjaman (id SERIAL PRIMARY KEY, anggota_id INTEGER NOT NULL REFERENCES anggota(id), buku_id INTEGER NOT NULL REFERENCES buku(id), tanggal_pinjam DATE NOT NULL, tanggal_kembali DATE);
INSERT INTO kategori (nama) VALUES ('Pemrograman'), ('Sains'), ('Sejarah'), ('Fiksi'), ('Statistika');
INSERT INTO anggota (nama, email, bergabung, aktif) VALUES ('Ali Rahman', '[email protected]', '2024-01-15', TRUE), ('Budi Santoso', '[email protected]', '2024-03-02', TRUE), ('Citra Kirana', '[email protected]', '2024-05-20', FALSE), ('Dewi Lestari', '[email protected]', '2024-08-11', TRUE), ('Eka Pratama', NULL, '2025-01-30', TRUE);
INSERT INTO buku (judul, penulis, tahun, kategori_id) VALUES ('Dasar Basis Data', 'Sari Wulandari', 2021, 1), ('Jaringan Komputer', 'Ahmad Fauzi', 2019, 1), ('Fisika untuk Pemula', 'Rina Marlina', 2020, 2), ('Kimia Organik', 'Rina Marlina', 2022, 2), ('Sejarah Nusantara', 'Bambang Wijaya', 2018, 3), ('Cerita Kota Lama', 'Nur Aisyah', 2023, 4), ('Statistika Terapan', 'Sari Wulandari', 2022, 5), ('Algoritma dan Struktur Data', 'Ahmad Fauzi', 2021, 1);
INSERT INTO peminjaman (anggota_id, buku_id, tanggal_pinjam, tanggal_kembali) VALUES (1, 1, '2025-03-01', '2025-03-15'), (1, 7, '2025-03-05', '2025-03-20'), (2, 3, '2025-03-10', NULL), (3, 5, '2025-02-20', '2025-03-01'), (2, 1, '2025-04-02', NULL), (4, 6, '2025-04-07', NULL), (5, 8, '2025-04-09', '2025-04-25'), (4, 2, '2025-05-01', NULL);
SQL

Struktur trigger dan fungsinya#

CREATE TRIGGER menautkan fungsi trigger ke peristiwa tabel, dan fungsi trigger ditulis dalam plpgsql dengan jenis kembalian khusus, yaitu trigger. Blok berikut membuat tabel riwayat, fungsi yang mencatat perubahan keaktifan anggota, dan trigger yang menjalankannya pada setiap UPDATE.

bash
# trigger yang mencatat perubahan keaktifan
psql -U postgres -d perpustakaan <<'SQL'
CREATE TABLE riwayat_anggota (
    id        SERIAL PRIMARY KEY,
    anggota_id INTEGER NOT NULL,
    catatan   TEXT NOT NULL,
    dicatat   DATE NOT NULL DEFAULT CURRENT_DATE
);
CREATE FUNCTION catat_perubahan_aktif()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    IF OLD.aktif IS DISTINCT FROM NEW.aktif THEN
        INSERT INTO riwayat_anggota (anggota_id, catatan)
        VALUES (NEW.id, 'aktif berubah menjadi ' || NEW.aktif);
    END IF;
    RETURN NEW;
END;
$$;
CREATE TRIGGER trg_aktif_anggota
AFTER UPDATE ON anggota
FOR EACH ROW EXECUTE FUNCTION catat_perubahan_aktif();
UPDATE anggota SET aktif = FALSE WHERE id = 1;
SELECT anggota_id, catatan FROM riwayat_anggota;
DROP TRIGGER trg_aktif_anggota ON anggota;
DROP FUNCTION catat_perubahan_aktif();
DROP TABLE riwayat_anggota;
SQL
text Keluaran
CREATE TABLE
CREATE FUNCTION
CREATE TRIGGER
UPDATE 1
 anggota_id |           catatan
------------+-----------------------------
          1 | aktif berubah menjadi false
(1 row)

DROP TRIGGER
DROP FUNCTION
DROP TABLE

Fungsi trigger membaca OLD untuk nilai lama baris dan NEW untuk nilai barunya, membandingkan keduanya dengan IS DISTINCT FROM agar perbandingan aman terhadap NULL, dan RETURN NEW meneruskan barisnya. Kata FOR EACH ROW menyatakan pemanggilan per baris, sedangkan FOR EACH STATEMENT memanggilnya sekali per pernyataan untuk pekerjaan yang tidak bergantung pada baris.

BEFORE untuk menegakkan dan mengubah#

Trigger BEFORE berjalan sebelum baris ditulis, dan nilai kembaliannya menentukan baris yang benar-benar ditulis, sehingga bentuknya tepat untuk memodifikasi atau menolak. Blok berikut menegakkan aturan penulisan email anggota selalu huruf kecil melalui trigger BEFORE INSERT yang mengubah nilainya.

bash
# BEFORE INSERT yang menormalkan email
psql -U postgres -d perpustakaan <<'SQL'
CREATE FUNCTION normalkan_email()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    NEW.email := lower(NEW.email);
    RETURN NEW;
END;
$$;
CREATE TRIGGER trg_email_anggota
BEFORE INSERT OR UPDATE ON anggota
FOR EACH ROW EXECUTE FUNCTION normalkan_email();
INSERT INTO anggota (nama, email, bergabung) VALUES ('Farah Nabila', '[email protected]', '2025-06-01') RETURNING nama, email;
DELETE FROM anggota WHERE email = '[email protected]';
DROP TRIGGER trg_email_anggota ON anggota;
DROP FUNCTION normalkan_email();
SQL
text Keluaran
CREATE FUNCTION
CREATE TRIGGER
     nama     |       email
--------------+-------------------
 Farah Nabila | [email protected]
(1 row)

INSERT 0 1
DELETE 1
DROP TRIGGER
DROP FUNCTION

Nilai yang ditulis tetap terbaca huruf kecil sebagaimana fungsi menetapkannya, karena perubahan pada NEW sebelum pernyataan tulis menjadi nilai yang sungguh tersimpan. Pemilihan BEFORE atau AFTER mengikuti tugasnya, yaitu perubahan dan penolakan dilakukan di BEFORE, sedangkan pencatatan atas hasil tulis dilakukan di AFTER.

Trigger pada pernyataan dan tampilan#

Selain UPDATE, peristiwa yang didengarkan trigger mencakup INSERT dan DELETE pada tabel, serta operasi TRUNCATE pada tingkat pernyataan. Peristiwa yang sama juga dapat dipasang pada view melalui trigger INSTEAD OF yang menuliskan kembali perubahan ke tabel dasarnya. Pilihan FOR EACH ROW dan FOR EACH STATEMENT mengikuti kebutuhannya, yaitu per baris untuk logika yang membaca nilai barisnya, dan sekali per pernyataan untuk ringkasan seperti pencatatan bahwa suatu pemuatan terjadi.

Urutan penjalanan beberapa trigger pada peristiwa yang sama mengikuti namanya secara abjad, kebiasaan yang dilayani dengan awalan penomoran bila urutannya penting. Trigger yang bertingkat, yaitu trigger yang menulis ke tabel yang juga memiliki trigger sendiri, tetap berjalan seperti penulisan biasa, dan kedalaman yang tidak terkendali menjadi tanda bahwa logikanya perlu diratakan.

Kesalahan umum#

Kesalahan pertama adalah menaruh logika penting hanya di trigger AFTER, padahal penolakan butuh BEFORE, karena AFTER tidak dapat mengubah barisnya yang sudah tersimpan. Kesalahan kedua adalah trigger yang menulis ke tabel yang sama dengan peristiwa pemicunya tanpa syarat berhenti, yang berputar tanpa akhir dan dihentikan server dengan pesan kedalaman pemicu.

Kesalahan ketiga adalah mengira trigger berjalan untuk setiap jalur penulisan tanpa memeriksa jenis peristiwanya, padahal trigger yang dideklarasikan hanya untuk INSERT tidak berjalan saat UPDATE, dan daftar peristiwanya dinyatakan eksplisit pada definisinya. Kesalahan keempat adalah logika bisnis yang berat di dalam trigger, yang membuat setiap penulisan membayarnya, sehingga trigger disisihkan untuk aturan yang wajib dan ringan, dan pekerjaan yang berat berpindah ke prosedur atau aplikasi.

Ringkasan#

  • CREATE TRIGGER menautkan fungsi trigger berjenis kembalian trigger ke peristiwa tabel yang dinyatakan eksplisit.
  • Fungsi trigger membaca OLD dan NEW, dan nilai kembaliannya pada BEFORE menentukan baris yang sungguh ditulis.
  • BEFORE untuk mengubah atau menolak, AFTER untuk pencatatan, dan FOR EACH ROW berbeda dari FOR EACH STATEMENT.
  • Trigger yang berputar dihentikan server, dan logika berat berpindah ke prosedur agar penulisan tetap ringan.

Referensi#