CodeGym /Các khóa học /SQL SELF /Tự động hóa công việc bằng trigger

Tự động hóa công việc bằng trigger

SQL SELF
Mức độ , Bài học
Có sẵn

Tự động hóa công việc bằng trigger

Hãy tưởng tượng bạn là admin của một database lớn. Đột nhiên ai đó xóa mất thông tin quan trọng trong bảng, và mọi người hét lên: "Ai làm vậy?!". Để tránh mấy vụ như thế, database cho phép ghi lại các thay đổi và theo dõi xem dữ liệu đã bị làm gì. Tất cả đều nhờ log và audit.

  • Ghi log thay đổi giúp lưu lại lịch sử: bản ghi nào bị thay đổi, thay đổi như thế nào và khi nào.
  • Audit dữ liệu dùng để kiểm tra sâu hơn, bao gồm cả việc lưu thông tin về người dùng đã thực hiện thay đổi đó.

Bây giờ bạn đã hiểu "tại sao" rồi, cùng học cách làm "như thế nào" nhé.

Tạo bảng log

Trước khi setup trigger, mình cần một bảng để lưu log thay đổi. Ví dụ như sau:

-- Tạo bảng để ghi log thay đổi
CREATE TABLE change_logs (
    log_id SERIAL PRIMARY KEY,       -- Định danh duy nhất cho mỗi bản ghi log
    table_name TEXT NOT NULL,        -- Tên bảng bị thay đổi
    operation TEXT NOT NULL,         -- Loại thao tác: INSERT, UPDATE, DELETE
    change_time TIMESTAMP DEFAULT NOW(), -- Thời gian thay đổi
    old_data JSONB,                  -- Dữ liệu trước khi thay đổi (cho UPDATE/DELETE)
    new_data JSONB                   -- Dữ liệu sau khi thay đổi (cho INSERT/UPDATE)
);

Ở đây có gì?

  1. log_id — định danh duy nhất cho mỗi bản ghi log.
  2. table_name — mình sẽ lưu tên bảng bị thay đổi.
  3. operation — loại thao tác: INSERT, UPDATE hoặc DELETE.
  4. change_time — lưu lại thời điểm thay đổi.
  5. old_datanew_data — dữ liệu trước và sau khi thay đổi ở dạng JSON.

Ghi log thay đổi bằng trigger

Bây giờ đã có bảng log rồi, cùng tạo trigger cho một bảng, ví dụ students. Trigger này sẽ ghi lại mọi thay đổi: thêm sinh viên mới, cập nhật hay xóa sinh viên. Cách làm như sau:

  1. Viết function bằng PL/pgSQL để thêm bản ghi vào bảng log.
  2. Tạo trigger trên bảng students.

Function sẽ nhận thông tin về thao tác (INSERT, UPDATE, DELETE), cùng với dữ liệu đã thay đổi (OLDNEW).

-- Function để ghi thay đổi vào bảng log
CREATE OR REPLACE FUNCTION log_student_changes()
RETURNS TRIGGER AS $$
BEGIN
    -- Ghi log thao tác INSERT
    IF TG_OP = 'INSERT' THEN
        INSERT INTO change_logs (table_name, operation, new_data)
        VALUES ('students', 'INSERT', row_to_json(NEW));

    -- Ghi log thao tác DELETE
    ELSIF TG_OP = 'DELETE' THEN
        INSERT INTO change_logs (table_name, operation, old_data)
        VALUES ('students', 'DELETE', row_to_json(OLD));

    -- Ghi log thao tác UPDATE
    ELSIF TG_OP = 'UPDATE' THEN
        INSERT INTO change_logs (table_name, operation, old_data, new_data)
        VALUES ('students', 'UPDATE', row_to_json(OLD), row_to_json(NEW));
    END IF;

    RETURN NULL; -- Trả về NULL vì đây là AFTER-trigger
END;
$$ LANGUAGE plpgsql;

Ở đây:

  • TG_OP — biến đặc biệt chứa thao tác hiện tại: INSERT, UPDATE, DELETE.
  • row_to_json(OLD)row_to_json(NEW) — chuyển dữ liệu dòng sang JSON cho dễ lưu trữ.
  • RETURN NULL — vì đây là AFTER-trigger nên không cần trả về dữ liệu đã thay đổi.

Bây giờ gắn function này với bảng students nhé.

-- Tạo trigger để ghi log thay đổi
CREATE TRIGGER students_log_trigger
AFTER INSERT OR UPDATE OR DELETE ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();

Ở đây có gì?

  • AFTER INSERT OR UPDATE OR DELETE — trigger chạy sau khi thực hiện các thao tác trên bảng students.
  • FOR EACH ROW — trigger chạy cho từng dòng bị thay đổi.
  • EXECUTE FUNCTION log_student_changes() — gọi function để ghi log.

Test trigger

Đến lúc test trigger rồi.

  1. Thêm bản ghi mới
INSERT INTO students (name, age, grade)
VALUES ('Otto Lin', 20, 'A');

Xem thử bảng log có gì:

SELECT * FROM change_logs;

Ví dụ kết quả:

log_id table_name operation change_time old_data new_data
1 students INSERT 2023-10-10 12:00:00 NULL {"name": "Otto Lin", "age": 20, ...}
  1. Cập nhật bản ghi
UPDATE students
SET grade = 'B'
WHERE name = 'Otto Lin';

Lại kiểm tra bảng log:

SELECT * FROM change_logs ORDER BY change_time DESC;

Kết quả:

log_id table_name operation change_time old_data new_data
2 students UPDATE 2023-10-10 12:05:00 {"name": "Otto Lin", "age": ...} {"name": "Otto Lin", "age": ..., ...}
  1. Xóa bản ghi
DELETE FROM students
WHERE name = 'Otto Lin';

Và lại kiểm tra log:

log_id table_name operation change_time old_data new_data
3 students DELETE 2023-10-10 12:10:00 {"name": "Otto Lin", "age": ...} NULL

Ví dụ thực tế

  1. Ghi log thao tác trên bảng quan trọng: ví dụ bảng tài khoản ngân hàng cần ghi lại mọi thay đổi để chống gian lận.
  2. Audit hệ thống: bạn có thể lưu log để tuân thủ quy định hoặc phân tích hành vi người dùng.
  3. Khôi phục dữ liệu: nếu ai đó lỡ tay xóa dữ liệu, bạn có thể phục hồi từ bảng log.

Lưu ý và cạm bẫy

Khi dùng trigger để ghi log, cần chú ý đến hiệu năng. Nếu trigger chạy quá thường xuyên, database sẽ bị tải nặng hơn. Vì vậy:

  • Chỉ ghi log trên các bảng thực sự quan trọng.
  • Nếu log quá nhiều, hãy nghĩ đến việc lưu trữ hoặc archive log.

Trigger giống như dây đàn guitar: cần chỉnh đúng thì mới hay, nhưng khi đã chuẩn thì tự động hóa công việc nhàm chán và kiểm soát dữ liệu cực tốt luôn.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION