PostgreSQL-də triggerlər üç əsas kateqoriyaya bölünür:
BEFORE— əsas əməliyyat yerinə yetirilməzdən əvvəl işə düşür (məsələn,INSERT,UPDATEvə yaDELETEqabağı). Onlardan istifadə edərək əməliyyatın qarşısını ala və ya məlumatları saxlanmazdan əvvəl dəyişə bilərsən.AFTER— əsas əməliyyat tamamlandıqdan sonra işə düşür. Bu tip adətən log yazmaq, əlaqəli qeydlər yaratmaq və ya əməliyyatın uğurla bitməsindən asılı olan hərəkətləri yerinə yetirmək üçün istifadə olunur.INSTEAD OF— faktiki əməliyyat əvəzinə işə düşür. Yalnız view-lar üçün istifadə olunur. Məsələn, istifadəçi view-a məlumat əlavə etməyə çalışanda, bu prosesiINSTEAD OFtrigger ilə idarə edə bilərsən.
BEFORE triggeri
BEFORE triggerləri PostgreSQL əsas əməliyyatı yerinə yetirməzdən əvvəl işə düşür. Əgər məlumatları saxlamazdan əvvəl yoxlamaq və ya dəyişmək istəyirsənsə, çox faydalıdır. Təsəvvür elə, bu elə bil təyyarəyə minməzdən əvvəl baqaj yoxlaması kimidir: əgər baqaj uyğun gəlmirsə, onu dəyişmək və ya ümumiyyətlə bloklamaq olar.
Gəlin, insert zamanı məlumatların doğruluğunu yoxlayan bir nümunəyə baxaq. Deyək ki, bizdə students adlı bir cədvəl var və burada tələbələr haqqında məlumat saxlayırıq. İstəyirik ki, tələbənin yaşı 100-dən çox olmasın (çox real deyil, düzdür).
Cədvəli yaradırıq:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
Trigger üçün funksiya yaradırıq:
CREATE OR REPLACE FUNCTION validate_age()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.age > 100 THEN
RAISE EXCEPTION 'Tələbənin yaşı 100-dən çox ola bilməz!';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Trigger yaradırıq:
CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION validate_age();
İndi, əgər sən 100-dən böyük yaşla tələbə əlavə etməyə çalışsan, PostgreSQL xəta verəcək:
INSERT INTO students (name, age) VALUES ('İvan İvanov', 120);
-- Xəta: Tələbənin yaşı 100-dən çox ola bilməz!
Bax belə bir yoxlama!
AFTER triggeri
AFTER triggerləri əsas əməliyyat uğurla başa çatdıqdan sonra işə düşür. Əməliyyatın nəticəsindən asılı olan hərəkətləri yerinə yetirmək üçün faydalıdır. Məsələn, log yazmaq və ya əlaqəli qeydlər yaratmaq üçün.
Ssenari: bizdə students cədvəli var və bütün dəyişiklikləri ayrıca log cədvəlinə yazmaq istəyirik.
Log üçün cədvəl yaradırıq:
CREATE TABLE students_log (
id SERIAL PRIMARY KEY,
student_id INT,
operation TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Trigger üçün funksiya yaradırıq:
CREATE OR REPLACE FUNCTION log_student_changes()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO students_log (student_id, operation)
VALUES (NEW.id, TG_OP); -- TG_OP əməliyyat tipini saxlayır: INSERT, UPDATE və ya DELETE
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Trigger yaradırıq:
CREATE TRIGGER after_insert_students
AFTER INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();
İndi yeni tələbə əlavə edəndə, PostgreSQL avtomatik olaraq əməliyyatı log-a yazacaq:
INSERT INTO students (name, age) VALUES ('Anna Linq', 22);
SELECT * FROM students_log;
-- Nəticə:
-- id | student_id | operation | timestamp
-- 1 | 1 | INSERT | 2023-11-15 12:00:00
INSTEAD OF triggeri
INSTEAD OF triggerləri əməliyyat yerinə yetirilməzdən əvvəl yox, onun əvəzinə işə düşür. Bu, yalnız view-lar üçün istifadə oluna bilən yeganə trigger tipidir. View-lar üzərində birbaşa əməliyyat aparmaq mümkün olmadıqda, bu triggerlər çox rahatdır.
Ssenari: bizdə iki cədvəl var — courses və teachers. Onları birləşdirən view yaradacağıq və bu view üzərindən insert əməliyyatını idarə edən trigger yazacağıq.
Cədvəlləri yaradırıq:
CREATE TABLE courses (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
teacher_id INT NOT NULL
);
CREATE TABLE teachers (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
View yaradırıq:
CREATE VIEW course_details AS
SELECT
courses.id AS course_id,
courses.name AS course_name,
teachers.name AS teacher_name
FROM courses
JOIN teachers ON courses.teacher_id = teachers.id;
Problem: view-a birbaşa məlumat əlavə edə bilmirik, çünki o, iki cədvəldən məlumatları birləşdirir. Həll: INSTEAD OF trigger istifadə edirik.
Trigger üçün funksiya yaradırıq:
CREATE OR REPLACE FUNCTION insert_course_details()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO teachers (name) VALUES (NEW.teacher_name) RETURNING id INTO NEW.teacher_id;
INSERT INTO courses (name, teacher_id) VALUES (NEW.course_name, NEW.teacher_id);
RETURN NULL; -- View-da məlumat saxlanmır
END;
$$ LANGUAGE plpgsql;
Trigger yaradırıq:
CREATE TRIGGER instead_of_insert_course_details
INSTEAD OF INSERT ON course_details
FOR EACH ROW
EXECUTE FUNCTION insert_course_details();
İndi məlumatı birbaşa view-a əlavə edə bilərsən:
INSERT INTO course_details (course_name, teacher_name)
VALUES ('Riyaziyyat', 'Aleks Minq');
SELECT * FROM courses;
-- Nəticə:
-- id | name | teacher_id
-- 1 | Riyaziyyat | 1
SELECT * FROM teachers;
-- Nəticə:
-- id | name
-- 1 | Aleks Minq
Trigger tiplərinin müqayisəsi
| Trigger tipi | Nə vaxt işə düşür | Əsas istifadə sahəsi |
|---|---|---|
BEFORE |
Əməliyyatdan əvvəl | Doğrulama, məlumatların hazırlanması |
AFTER |
Uğurlu başa çatdıqdan sonra | Log yazmaq, əlaqəli məlumatların yenilənməsi |
INSTEAD OF |
Əməliyyat əvəzinə | View-larda əməliyyatların işlənməsi |
Xüsusiyyətlər və məhdudiyyətlər
BEFORE triggerləri əməliyyatdan əvvəl məlumatları dəyişə bilər. Məsələn, adları avtomatik olaraq böyük hərflə yaza bilərsən.
AFTER triggerləri məlumatlara təsir edə bilmir, çünki əməliyyat artıq başa çatıb. Onlar yalnız sonrakı hərəkətlər üçündür.
INSTEAD OF triggerləri yalnız view-lara tətbiq olunur. Onlar bir neçə əlaqəli cədvəldə mürəkkəb insert/update məntiqini reallaşdırmağa imkan verir.
Bu günlük bu qədər! Əgər BEFORE, AFTER və INSTEAD OF bir az çətin görünürsə, narahat olma. Əsas odur ki, onların əsas prinsiplərini və istifadə ssenarilərini yadında saxla. Bir neçə nümunəni özün yazmağa çalış, material daha yaxşı yadda qalacaq.
GO TO FULL VERSION