Trigger — BEFORE, AFTER, INSTEAD OF, trigger funksiya
Muammodan boshlaylik
Foydalanuvchi emailini o'zgartirdi. Siz bu o'zgarishni audit_log jadvaliga yozmoqchisiz — kim, qachon, nima o'zgartirdi. Buning uchun ilovada har qayerda "email o'zgartirilganda log yozish" kodini qo'shish kerak. Lekin kimdir unutsa-chi? Yoki boshqa ilova ham bir xil jadvalni o'zgartirsachi?
Yechim: bu logikani bazaning o'ziga qo'yish. Jadval qanday o'zgartirilishidan qat'i nazar — bazaning o'zi log yozsin. Bu — Trigger.
Trigger nima?
Trigger — jadvalda biror hodisa (INSERT, UPDATE, DELETE) bo'lganda avtomatik ishlaydigan funksiya. Siz chaqirmaysiz, baza o'zi chaqiradi.
Hayotiy o'xshatma: eshikdagi sensor. Odam kirganda chiroq yonadi — siz tugmani bosmaysiz, sensor o'zi sezadi va chiroqni yoqadi. Trigger — shu sensor. Jadvalda o'zgarish — sensor sezdi. Trigger funksiya — chiroq yondi.
Oddiy yondashuv: Trigger bilan:
────────────────────────────── ──────────────────────────────
Ilova kodi → UPDATE Ilova kodi → UPDATE
Ilova kodi → log yozish Trigger ← baza o'zi yozadi
Ilova kodi 2 → UPDATE Ilova kodi 2 → UPDATE
Ilova kodi 2 → log yozish Trigger ← baza o'zi yozadi
Unutilsa → log yo'q ❌ Har doim yoziladi ✅
Trigger qanday ishlaydi
Trigger — ikki qismdan iborat:
1. Trigger funksiya — nima qilish kerakligini bildiradi. Oddiy funksiyaga o'xshash, lekin RETURNS TRIGGER qaytaradi.
2. Trigger o'zi — qachon va qaysi jadvalda ishlatilishini bildiradi.
Jadvalda hodisa
↓
Trigger ishga tushadi
↓
Trigger funksiya chaqiriladi
↓
Funksiya natijasiga qarab asl operatsiya davom etadi yoki bekor bo'ladi
Trigger funksiya
Trigger funksiya oddiy funksiyadan ikki narsa bilan farqlanadi:
RETURNS TRIGGER— qaytish turi har doim shu- Maxsus o'zgaruvchilar —
NEW,OLD,TG_OPva boshqalar
CREATE OR REPLACE FUNCTION trigger_funksiya_nomi()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $
BEGIN
-- NEW → yangi qiymatlar (INSERT va UPDATE da bor)
-- OLD → eski qiymatlar (UPDATE va DELETE da bor)
-- TG_OP → operatsiya turi: 'INSERT', 'UPDATE', 'DELETE'
RETURN NEW; -- BEFORE triggerda bu muhim
END;
$;
Trigger yaratish
CREATE TRIGGER trigger_nomi
{BEFORE | AFTER | INSTEAD OF} -- qachon
{INSERT | UPDATE | DELETE | ...} -- qaysi hodisa
ON jadval_nomi
FOR EACH {ROW | STATEMENT} -- har qator uchun yoki bir marta
EXECUTE FUNCTION trigger_funksiya_nomi();
BEFORE trigger — operatsiyadan oldin
BEFORE trigger operatsiya bajarilishidan oldin ishga tushadi. Funksiyadan qaytgan NEW qiymati asl jadvalga yoziladi — shu sababli BEFORE da ma'lumotni o'zgartirib, tozalab, validatsiya qilish mumkin.
Misol: email ni kichik harfga o'girish
Foydalanuvchi emailni qanday yozmaysin — baza doim kichik harfda saqlasin:
CREATE OR REPLACE FUNCTION email_kichik_harf()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $
BEGIN
NEW.email := LOWER(TRIM(NEW.email)); -- ← NEW ni o'zgartirdik
RETURN NEW; -- ← o'zgartirilgan NEW jadvalga yoziladi
END;
$;
CREATE TRIGGER foydalanuvchi_email_trigger
BEFORE INSERT OR UPDATE ON foydalanuvchilar
FOR EACH ROW
EXECUTE FUNCTION email_kichik_harf();
Endi:
INSERT INTO foydalanuvchilar (ism, email) VALUES ('Ali', ' ALI@MAIL.COM ');
SELECT email FROM foydalanuvchilar WHERE ism = 'Ali';
-- ali@mail.com ← avtomatik tozalandi va kichik harfga o'girildi
Misol: BEFORE da bekor qilish
Agar BEFORE trigger NULL qaytarsa — operatsiya umuman bajarilmaydi:
CREATE OR REPLACE FUNCTION narx_tekshir()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $
BEGIN
IF NEW.narx < 0 THEN
RAISE EXCEPTION 'Narx manfiy bo''lishi mumkin emas: %', NEW.narx;
-- yoki: RETURN NULL; -- xatosiz bekor qilish
END IF;
RETURN NEW;
END;
$;
CREATE TRIGGER mahsulot_narx_trigger
BEFORE INSERT OR UPDATE ON mahsulotlar
FOR EACH ROW
EXECUTE FUNCTION narx_tekshir();
AFTER trigger — operatsiyadan keyin
AFTER trigger operatsiya bajarilgandan keyin ishga tushadi. Ma'lumot allaqachon yozilgan — uni o'zgartirish mumkin emas. Log yozish, boshqa jadvalni yangilash, xabar yuborish uchun ishlatiladi.
Misol: o'zgarishlarni log qilish
Kim, qachon, nima o'zgartirganini saqlash:
-- Avval log jadvali
CREATE TABLE foydalanuvchi_log (
id SERIAL PRIMARY KEY,
amal TEXT, -- INSERT, UPDATE, DELETE
eski_email TEXT,
yangi_email TEXT,
sana TIMESTAMPTZ DEFAULT NOW()
);
CREATE OR REPLACE FUNCTION foydalanuvchi_log_yoz()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $
BEGIN
INSERT INTO foydalanuvchi_log (amal, eski_email, yangi_email)
VALUES (
TG_OP, -- 'INSERT', 'UPDATE', 'DELETE'
CASE WHEN TG_OP != 'INSERT' THEN OLD.email END, -- eski qiymat
CASE WHEN TG_OP != 'DELETE' THEN NEW.email END -- yangi qiymat
);
RETURN NULL; -- AFTER trigger da bu ahamiyatsiz
END;
$;
CREATE TRIGGER foydalanuvchi_log_trigger
AFTER INSERT OR UPDATE OR DELETE ON foydalanuvchilar
FOR EACH ROW
EXECUTE FUNCTION foydalanuvchi_log_yoz();
Endi:
INSERT INTO foydalanuvchilar (ism, email) VALUES ('Ali', 'ali@mail.com');
UPDATE foydalanuvchilar SET email = 'ali2@mail.com' WHERE ism = 'Ali';
DELETE FROM foydalanuvchilar WHERE ism = 'Ali';
SELECT * FROM foydalanuvchi_log;
id │ amal │ eski_email │ yangi_email
────┼────────┼────────────────┼────────────────
1 │ INSERT │ NULL │ ali@mail.com
2 │ UPDATE │ ali@mail.com │ ali2@mail.com
3 │ DELETE │ ali2@mail.com │ NULL
FOR EACH ROW va FOR EACH STATEMENT
FOR EACH ROW: FOR EACH STATEMENT:
────────────────────────────────── ───────────────────────────────
Har bir o'zgartirilgan qator uchun Butun operatsiya uchun bir marta
NEW va OLD bor NEW va OLD yo'q
100 qator o'zgarsa — 100 marta 100 qator o'zgarsa — 1 marta
Ko'pchilik holatlarda FOR EACH ROW ishlatiladi. FOR EACH STATEMENT — ko'p qatorli operatsiyaning umumiy holatini bilish kerak bo'lganda.
INSTEAD OF trigger — VIEW uchun
Oddiy VIEW ga INSERT/UPDATE/DELETE qilib bo'lmaydi. INSTEAD OF trigger VIEW ga yozishni real jadvalga yo'naltiradi.
-- View: foydalanuvchi + profil birgalikda
CREATE VIEW foydalanuvchi_profil AS
SELECT
f.id, f.email,
p.ism, p.bio
FROM foydalanuvchilar f
JOIN profillar p ON f.id = p.foydalanuvchi_id;
-- View ga INSERT qilish uchun trigger
CREATE OR REPLACE FUNCTION foydalanuvchi_profil_insert()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $
DECLARE
yangi_id INT;
BEGIN
INSERT INTO foydalanuvchilar (email) VALUES (NEW.email)
RETURNING id INTO yangi_id;
INSERT INTO profillar (foydalanuvchi_id, ism, bio)
VALUES (yangi_id, NEW.ism, NEW.bio);
RETURN NEW;
END;
$;
CREATE TRIGGER foydalanuvchi_profil_insert_trigger
INSTEAD OF INSERT ON foydalanuvchi_profil
FOR EACH ROW
EXECUTE FUNCTION foydalanuvchi_profil_insert();
Endi:
-- View ga INSERT — trigger ikki jadvalni to'ldiradi
INSERT INTO foydalanuvchi_profil (email, ism, bio)
VALUES ('ali@mail.com', 'Ali', 'Dasturchi');
Trigger ni ko'rish va o'chirish
-- Jadvalning triggerlarini ko'rish
SELECT trigger_name, event_manipulation, action_timing
FROM information_schema.triggers
WHERE event_object_table = 'foydalanuvchilar';
-- O'chirish
DROP TRIGGER trigger_nomi ON jadval_nomi;
-- Vaqtincha o'chirish
ALTER TABLE foydalanuvchilar DISABLE TRIGGER trigger_nomi;
ALTER TABLE foydalanuvchilar ENABLE TRIGGER trigger_nomi;
Tez-tez uchraydigan xatolar
BEFORE trigger da RETURN NEW ni unutish:
-- ❌ — RETURN yo'q yoki RETURN NULL — operatsiya bekor bo'ladi!
CREATE FUNCTION ... RETURNS TRIGGER AS $
BEGIN
NEW.email := LOWER(NEW.email);
-- RETURN yo'q → INSERT/UPDATE bajarilmaydi!
END;
$;
-- ✅ — RETURN NEW shart
BEGIN
NEW.email := LOWER(NEW.email);
RETURN NEW; -- o'zgartirilgan qator yoziladi
END;
BEFORE trigger da RETURN NEW yozilmasa — yoki RETURN NULL qaytarilsa — operatsiya bajarilmaydi. Bu eng ko'p uchraydigan xato.
Trigger funksiyasi cheksiz o'zini chaqirishi:
-- ❌ — trigger UPDATE qiladi, UPDATE trigger ni qayta ishga tushiradi...
CREATE TRIGGER ...
AFTER UPDATE ON foydalanuvchilar
FOR EACH ROW
EXECUTE FUNCTION ...;
-- Trigger funksiyasi ichida:
UPDATE foydalanuvchilar SET ... -- ← yana triggerni ishga tushiradi!
Trigger funksiyasi ichida o'sha jadvalga yozish — cheksiz rekursiya. pg_trigger_depth() bilan tekshirib oldini olish:
IF pg_trigger_depth() > 1 THEN RETURN NEW; END IF;
Xulosa
Trigger — jadvalda hodisa bo'lganda bazaning o'zi chaqiradigan funksiya. Ilova kodidan mustaqil, har doim ishlaydi.
BEFORE → operatsiyadan oldin, NEW ni o'zgartirish mumkin
AFTER → operatsiyadan keyin, log yozish, boshqa jadvalni yangilash
INSTEAD OF → VIEW ga yozish uchun
Qachon trigger kerak:
- Ma'lumotni avtomatik tozalash yoki normallashtirish (email, telefon)
- Audit log — kim, qachon, nima o'zgartirdi
- Bog'liq jadvallarni sinxronlashtirish
- VIEW ga yozish imkonini berish
Qachon trigger kerak emas:
- Ilova kodi osongina hal qiladigan narsalar — trigger yashirin logika, debugging qiyin
- Juda murakkab biznes logika — procedure yoki ilova kodi aniqroq
Keyingi dars
35-dars: VIEW va MATERIALIZED VIEW — farqi, REFRESH