Тригери у PostgreSQL діляться на три основні категорії:
BEFORE— виконуються перед виконанням основної операції (наприклад, передINSERT,UPDATEчиDELETE). Ти можеш використовувати їх, щоб запобігти виконанню операції або змінити дані перед їх збереженням.AFTER— виконуються після того, як основна операція завершена. Цей тип часто використовують для логування, створення пов’язаних записів або виконання дій, які залежать від успішного завершення операції.INSTEAD OF— виконуються замість фактичної операції. Використовуються тільки для представлень. Наприклад, якщо користувач намагається вставити дані у представлення, ти можеш керувати цим процесом за допомогою тригераINSTEAD OF.
Тригер BEFORE
Тригери BEFORE спрацьовують до того, як PostgreSQL виконає основну операцію. Вони корисні, якщо ти хочеш перевірити або змінити дані безпосередньо перед їх збереженням. Уяви собі, що це як перевірка багажу перед посадкою на літак: якщо багаж не підходить, його можна змінити або взагалі заблокувати.
Давай наведемо приклад з валідацією даних перед вставкою. Нехай у нас є таблиця students, у якій ми зберігаємо інформацію про студентів. Ми хочемо впевнитися, що вік студента не перевищує 100 років (малоймовірно, звісно).
Створюємо таблицю:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
Створюємо функцію для тригера:
CREATE OR REPLACE FUNCTION validate_age()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.age > 100 THEN
RAISE EXCEPTION 'Вік студента не може бути більше 100 років!';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Створюємо тригер:
CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION validate_age();
Тепер, якщо ти спробуєш вставити студента з віком більше 100, PostgreSQL видасть помилку:
INSERT INTO students (name, age) VALUES ('Іван Іванов', 120);
-- Помилка: Вік студента не може бути більше 100 років!
Ось така перевірка!
Тригер AFTER
Тригери AFTER спрацьовують після того, як основна операція завершена успішно. Вони корисні для виконання дій, які залежать від результату операції. Наприклад, логування або створення пов’язаних записів.
Сценарій: у нас є таблиця students, і ми хочемо записувати всі зміни в окрему таблицю-лог.
Створюємо таблицю для логів:
CREATE TABLE students_log (
id SERIAL PRIMARY KEY,
student_id INT,
operation TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Створюємо функцію для тригера:
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 містить тип операції: INSERT, UPDATE чи DELETE
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Створюємо тригер:
CREATE TRIGGER after_insert_students
AFTER INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();
Тепер, коли ти додаєш нового студента, PostgreSQL автоматично записує операцію в лог:
INSERT INTO students (name, age) VALUES ('Анна Лінг', 22);
SELECT * FROM students_log;
-- Результат:
-- id | student_id | operation | timestamp
-- 1 | 1 | INSERT | 2023-11-15 12:00:00
Тригер INSTEAD OF
Тригери INSTEAD OF спрацьовують замість виконання операції. Це єдиний тип тригерів, який можна використовувати з представленнями (views). Вони дають гнучкість в обробці операцій, які не можна виконати напряму на представленні.
Сценарій: у нас є дві таблиці courses та teachers. Ми створимо представлення, яке об’єднує їх, і напишемо тригер для обробки операцій вставки через це представлення.
Створюємо таблиці:
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
);
Створюємо представлення:
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;
Проблема: ми не можемо просто так вставити дані у представлення, бо воно агрегує дані з двох таблиць. Рішення: використовуємо тригер INSTEAD OF.
Створюємо функцію для тригера:
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; -- У представленні дані не зберігаються
END;
$$ LANGUAGE plpgsql;
Створюємо тригер:
CREATE TRIGGER instead_of_insert_course_details
INSTEAD OF INSERT ON course_details
FOR EACH ROW
EXECUTE FUNCTION insert_course_details();
Тепер ти можеш вставляти дані напряму у представлення:
INSERT INTO course_details (course_name, teacher_name)
VALUES ('Математика', 'Алекс Мінг');
SELECT * FROM courses;
-- Результат:
-- id | name | teacher_id
-- 1 | Математика | 1
SELECT * FROM teachers;
-- Результат:
-- id | name
-- 1 | Алекс Мінг
Порівняння типів тригерів
| Тип тригера | Коли виконується | Основне застосування |
|---|---|---|
BEFORE |
До виконання операції | Валідація, підготовка даних |
AFTER |
Після успішного завершення | Логування, оновлення пов’язаних даних |
INSTEAD OF |
Замість виконання операції | Обробка операцій на представленнях |
Особливості та обмеження
BEFORE тригери можуть змінювати дані перед виконанням операції. Наприклад, ти можеш автоматично форматувати імена (робити їх великими літерами).
AFTER тригери не можуть вплинути на дані, бо операція вже завершена. Вони виключно для подальших дій.
INSTEAD OF тригери застосовуються тільки до представлень. Вони дозволяють реалізовувати складну логіку вставки/зміни даних у кількох пов’язаних таблицях.
Ось і все на сьогодні! Якщо BEFORE, AFTER і INSTEAD OF здаються складними — не переймайся. Головне — пам’ятати їхній основний принцип і сценарії використання. Спробуй реалізувати кілька прикладів самостійно, щоб закріпити матеріал.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ