CodeGym /Kurslar /SQL SELF /Trigger-lərin PL/pgSQL funksiyaları ilə qarşılıqlı əlaqəs...

Trigger-lərin PL/pgSQL funksiyaları ilə qarşılıqlı əlaqəsi: OLD, NEW, TG_OP

SQL SELF
Səviyyə , Dərs
Mövcuddur

PostgreSQL-də trigger-lər təkcə hansısa əməliyyata cavab olaraq funksiyanı işə salmır, həm də bu funksiyalara möhtəşəm dəyişənlər ötürür. Məhz bu dəyişənlər sayəsində cədvəldəki məlumatların əməliyyatdan əvvəl necə olduğunu, sonra necə dəyişdiyini və ümumiyyətlə hansı əməliyyatın baş verdiyini bilmək olur.

  • OLD — əməliyyatdan əvvəl cədvəlin sətrinin köhnə məlumatlarını saxlayır. UPDATEDELETE trigger-lərində istifadə olunur, çünki INSERT zamanı "köhnə" heç nə olmur.
  • NEW — əməliyyatdan sonra cədvəlin sətrinin yeni məlumatlarını saxlayır. INSERTUPDATE trigger-lərində istifadə olunur.
  • TG_OP — cari əməliyyat barədə mətn məlumatı saxlayır: INSERT, UPDATE və ya DELETE.

Bütün bu dəyişənlər trigger-lə bağlı funksiyanın içində avtomatik olaraq əlçatandır.

Teoriya praktikadan ayrı olsa, SQL indekssiz kimi olur: yavaş və sıxıcı. Ona görə gəlin praktiki nümunələrə baxaq.

OLD ilə köhnə məlumatlara baxış

Təsəvvür elə ki, bizdə students adlı cədvəl var. Və orada kimsə tələbənin yaşını dəyişir (birdən başında səhv qalıb ki, tələbə hələ 20 yaşındadır, halbuki artıq 25-dir).

CREATE TABLE students (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    age INT NOT NULL
);

Dəyişiklikləri izləmək üçün log cədvəli yaradaq:

CREATE TABLE student_changes (
    change_id SERIAL PRIMARY KEY,
    student_id INT NOT NULL,
    old_value INT,
    new_value INT,
    change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

İndi isə, dəyişiklikləri qeyd edəcək funksiya yaradaq. Burada OLD çox işə yarayacaq:

CREATE OR REPLACE FUNCTION log_student_changes()
RETURNS TRIGGER AS $$
BEGIN
    -- Yaş dəyişikliklərini log-layırıq
    INSERT INTO student_changes (student_id, old_value, new_value)
    VALUES (OLD.id, OLD.age, NEW.age);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

İndi isə trigger yaradaq:

CREATE TRIGGER student_age_update
AFTER UPDATE OF age ON students
FOR EACH ROW
WHEN (OLD.age IS DISTINCT FROM NEW.age) -- Yalnız yaş dəyişibsə işləsin
EXECUTE FUNCTION log_student_changes();

Gəlin tələbə əlavə edək və sonra dəyişiklik edək:

INSERT INTO students (name, age) VALUES ('Alisa', 20);

UPDATE students
SET age = 25
WHERE name = 'Alisa';

-- Dəyişiklik logunu yoxlayaq:
SELECT * FROM student_changes;

Görəcəksən ki, log cədvəlinə dəyişiklik yazılıb: yaş 20-dən 25-ə keçib. Sehr? Yox, OLD!

NEW ilə yeni məlumatlara baxış

İndi isə təsəvvür elə ki, yeni tələbə əlavə olunanda avtomatik olaraq onun ID və adını log cədvəlinə yazmaq istəyirik (bəli, bir az paranoyadır, amma bəzən faydalıdır):

CREATE OR REPLACE FUNCTION log_new_student()
RETURNS TRIGGER AS $$
BEGIN
    -- Yeni tələbənin məlumatlarını log-layırıq
    INSERT INTO student_changes (student_id, old_value, new_value)
    VALUES (NEW.id, NULL, NEW.age); -- Köhnə dəyər yoxdur, çünki bu INSERT-dir
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER student_insert_log
AFTER INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION log_new_student();

Yenə də, yeni tələbə əlavə edək və yoxlayaq:

INSERT INTO students (name, age) VALUES ('Bob', 22);

-- Log-u yoxlayaq:
SELECT * FROM student_changes;

Görəcəksən ki, logda yeni tələbə var. Bax belə, məlumatlara yeni səviyyədə qayğı!

TG_OP ilə əməliyyat növünü müəyyən etmək

Bəs əgər universal loglama trigger-i istəyirik ki, həm INSERT, həm UPDATE, hətta DELETE üçün işləsin? Burada TG_OP dəyişəni köməyə gəlir.

Universal funksiya yaradaq:

CREATE OR REPLACE FUNCTION log_all_operations()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'INSERT' THEN
        INSERT INTO student_changes (student_id, old_value, new_value)
        VALUES (NEW.id, NULL, NEW.age);

    ELSIF TG_OP = 'UPDATE' THEN
        INSERT INTO student_changes (student_id, old_value, new_value)
        VALUES (OLD.id, OLD.age, NEW.age);

    ELSIF TG_OP = 'DELETE' THEN
        INSERT INTO student_changes (student_id, old_value, new_value)
        VALUES (OLD.id, OLD.age, NULL);

    END IF;

    RETURN NULL; -- AFTER trigger-də DELETE üçün NULL qaytarırıq
END;
$$ LANGUAGE plpgsql;

İndi isə bütün üç əməliyyatda işləyəcək trigger yaradaq:

CREATE TRIGGER universal_student_log
AFTER INSERT OR UPDATE OR DELETE ON students
FOR EACH ROW
EXECUTE FUNCTION log_all_operations();

Tələbə əlavə edək, dəyişək və silək:

INSERT INTO students (name, age) VALUES ('Çarli', 30);
UPDATE students SET age = 31 WHERE name = 'Çarli';
DELETE FROM students WHERE name = 'Çarli';

-- Log-u yoxlayaq:
SELECT * FROM student_changes;

Bütün əməliyyatların logunu görə biləcəksən — bir trigger, hamısına nəzarət!

OLD, NEW, TG_OP ilə tipik səhvlər

Trigger-lərlə işləyəndə bir neçə yayılmış problemə rast gəlmək olar:

"Niyə OLD insert zamanı işləmir?" Bu default davranışdır: INSERT üçün köhnə məlumat yoxdur. NEW istifadə et.

"Bəs NEW delete zamanı işləmir?" Yenə də, bu gözlənilən davranışdır: DELETE üçün yeni məlumat yoxdur. OLD istifadə et.

Trigger-in məntiqi sonsuz rekursiyaya səbəb olur. Trigger-in təsadüfən özünü çağırmamasına diqqət et. Bunun üçün WHEN blokunda dəqiq şərtlər yaz və ya TG_OP-u yoxla.

1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Trigerlərə giriş
Trigerlərə giriş
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION