Wyobraź sobie, że robisz apkę do zarządzania studentami i kursami, i masz tabelę students. W tej tabeli jest pole last_modified, które powinno się automatycznie aktualizować za każdym razem, gdy zmieniasz dane w rekordzie (np. zmiana imienia studenta albo jego wieku).
Zamiast ręcznie dopisywać aktualizację last_modified w każdym zapytaniu SQL, zrobimy trigger, który ogarnie to za nas.
Struktura tabeli students
Na początek stwórzmy tabelę students, której będziemy używać w przykładzie. Ta tabela zawiera podstawowe info o studentach:
CREATE TABLE students (
student_id SERIAL PRIMARY KEY, -- Unikalny identyfikator studenta
name VARCHAR(100) NOT NULL, -- Imię studenta
age INT, -- Wiek studenta
last_modified TIMESTAMP NOT NULL DEFAULT NOW() -- Czas ostatniej zmiany
);
- Pole
last_modifiedna starcie wypełnia się aktualnym czasem (NOW()) przy tworzeniu rekordu. - To pole będzie się automatycznie aktualizować przy zmianie danych studenta.
Wrzucamy do tabeli trochę testowych danych:
INSERT INTO students (name, age)
VALUES
('Otto Lin', 20),
('Maria Chi', 22),
('Aleks Song', 19);
Teraz dane w tabeli wyglądają tak:
| student_id | name | age | last_modified |
|---|---|---|---|
| 1 | Otto Lin | 20 | 2023-10-15 12:00:00 |
| 2 | Maria Chi | 22 | 2023-10-15 12:00:00 |
| 3 | Aleks Song | 19 | 2023-10-15 12:00:00 |
Tworzenie funkcji do aktualizacji last_modified
Funkcja w PL/pgSQL będzie używana przez trigger do aktualizowania wartości pola last_modified. Odpali się automatycznie przed zmianą rekordu.
Tworzymy funkcję update_last_modified:
CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
-- Aktualizujemy pole last_modified na aktualny czas
NEW.last_modified := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
NEW— to specjalna zmienna, która trzyma nowe dane rekordu (po zmianie).- Ustawiamy
NEW.last_modifiednaNOW()(aktualna data i godzina). - Funkcja zwraca zaktualizowaną zmienną
NEW, co jest potrzebne, żeby trigger działał poprawnie.
Tworzenie triggera
Teraz robimy trigger, który automatycznie odpali funkcję update_last_modified przy każdej aktualizacji rekordu w tabeli students.
CREATE TRIGGER set_last_modified
BEFORE UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();
Co tu się dzieje:
BEFORE UPDATEoznacza, że trigger odpala się przed wykonaniem operacji UPDATE.FOR EACH ROWznaczy, że trigger działa dla każdego zmienianego rekordu.EXECUTE FUNCTION update_last_modified()mówi, żeby wywołać funkcjęupdate_last_modified.
Testowanie triggera
Sprawdźmy teraz, jak działa nasz trigger. Wybierzmy aktualne dane z tabeli students:
SELECT * FROM students;
Wynik:
| student_id | name | age | last_modified |
|---|---|---|---|
| 1 | Otto Lin | 20 | 2023-10-15 12:00:00 |
| 2 | Maria Chi | 22 | 2023-10-15 12:00:00 |
| 3 | Aleks Song | 19 | 2023-10-15 12:00:00 |
Teraz zaktualizujemy wiek studenta z student_id = 1:
UPDATE students
SET age = 21
WHERE student_id = 1;
Znowu wybieramy dane z tabeli:
SELECT * FROM students;
Oczekiwany wynik:
| student_id | name | age | last_modified |
|---|---|---|---|
| 1 | Otto Lin | 21 | 2023-10-15 14:00:00 |
| 2 | Maria Chi | 22 | 2023-10-15 12:00:00 |
| 3 | Aleks Song | 19 | 2023-10-15 12:00:00 |
Zwróć uwagę: pole last_modified dla rekordu z student_id = 1 zaktualizowało się na aktualny czas, a reszta rekordów została bez zmian.
Rozszerzenie logiki triggera
Załóżmy, że teraz chcemy, żeby pole last_modified aktualizowało się tylko jeśli zmienią się konkretne kolumny. Na przykład, jeśli zmienia się tylko imię studenta albo jego wiek, trigger ma się odpalić, a w innych przypadkach — nie.
Do tego można dodać warunek z operatorem WHEN w definicji triggera.
Tworzymy nowy trigger z warunkiem:
DROP TRIGGER IF EXISTS set_last_modified ON students;
CREATE TRIGGER set_last_modified
BEFORE UPDATE ON students
FOR EACH ROW
WHEN (OLD.name IS DISTINCT FROM NEW.name OR OLD.age IS DISTINCT FROM NEW.age)
EXECUTE FUNCTION update_last_modified();
Tu:
- Warunek
WHENsprawdza, czy stare wartości (OLD) różnią się od nowych (NEW) dla kolumnnameiage. - Jeśli żadna z tych kolumn się nie zmieniła, trigger się nie odpala.
Spróbujmy znowu zaktualizować dane w tabeli i przetestować nową logikę.
Wskazówki dotyczące używania triggerów
- Nie przesadzaj z triggerami. Są wygodne, ale mogą skomplikować logikę bazy i utrudnić debugowanie.
- Zawsze dokumentuj, co robi trigger i w jakich przypadkach jest używany.
- Używaj warunków
WHEN, żeby zminimalizować przypadkowe odpalenia triggera. - Pamiętaj, że triggery mogą wpływać na wydajność bazy, szczególnie jeśli tabela ma dużo rekordów.
Typowe błędy przy pracy z triggerami
Zła zmiana danych. Na przykład, zapomniałeś ustawić wartość dla NEW i zwróciłeś oryginalne dane bez zmian.
Złe warunki. Na przykład, zapomniałeś dodać warunku WHEN i trigger odpala się nawet wtedy, gdy nie trzeba nic zmieniać.
Rekursja. Jeśli trigger wywołuje funkcję, która znowu wywołuje trigger, możesz przypadkiem zrobić nieskończoną pętlę. PostgreSQL ma ochronę przed rekursją, ale lepiej unikać takich sytuacji.
Ten przykład pokazuje, jak użycie triggerów może uprościć automatyczne aktualizowanie danych. W prawdziwych projektach taka technika jest często używana do logowania zmian, pilnowania spójności danych i automatyzacji nudnych rzeczy.
GO TO FULL VERSION