Nếu bạn từng gặp trường hợp cần thực hiện một hành động cho từng dòng khi cập nhật dữ liệu hàng loạt, hoặc chỉ một lần cho cả bảng, thì chắc cũng từng băn khoăn: nên làm thế nào cho hợp lý? PostgreSQL cho bạn hai lựa chọn: trigger cấp dòng và trigger cấp thao tác. Hiểu khi nào dùng mỗi loại là rất quan trọng để thiết kế database đúng, tối ưu hiệu năng và tránh lỗi. Cùng tìm hiểu nhé!
Trigger hoạt động ở cấp dòng (FOR EACH ROW) sẽ chạy mỗi lần cho từng dòng bị ảnh hưởng bởi thao tác INSERT, UPDATE hoặc DELETE. Nghĩa là nếu câu lệnh SQL ảnh hưởng đến 100 dòng, trigger sẽ chạy 100 lần.
Khi nào nên dùng?
Trigger cấp dòng hữu ích khi bạn cần xử lý từng dòng thay đổi riêng biệt. Ví dụ:
- Ghi log thay đổi cho từng dòng.
- Tự động cập nhật dữ liệu liên quan cho từng dòng.
Ví dụ: ghi log thay đổi cho từng dòng
Giả sử bạn có bảng sinh viên:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
age INT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Ta muốn ghi log từng dòng được cập nhật vào bảng riêng students_log:
CREATE TABLE students_log (
log_id SERIAL PRIMARY KEY,
student_id INT,
old_name VARCHAR(100),
new_name VARCHAR(100),
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Hàm để ghi log thay đổi:
CREATE OR REPLACE FUNCTION log_student_update()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO students_log(student_id, old_name, new_name, changed_at)
VALUES (OLD.id, OLD.name, NEW.name, CURRENT_TIMESTAMP);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Tạo trigger FOR EACH ROW:
CREATE TRIGGER student_update_logger
AFTER UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_update();
Test thử:
UPDATE students
SET name = 'Ivan Ivanov'
WHERE id = 1;
Sau khi chạy lệnh này, bảng students_log sẽ có một bản ghi chi tiết về thay đổi.
Trigger cấp thao tác (FOR EACH STATEMENT)
Trigger cấp thao tác (FOR EACH STATEMENT) chỉ chạy một lần cho toàn bộ câu lệnh SQL, bất kể có bao nhiêu dòng bị ảnh hưởng. Nếu câu lệnh update 100 dòng, trigger vẫn chỉ chạy một lần.
Trigger cấp thao tác hữu ích khi bạn cần:
- Chỉ thực hiện hành động một lần cho cả thao tác.
- Làm việc với dữ liệu tổng hợp hoặc tính toán cho cả bảng.
Ví dụ: cập nhật bộ đếm thay đổi
Giả sử bạn có bảng đếm số lần thay đổi cho bảng students:
CREATE TABLE students_changes_log (
total_changes INT DEFAULT 0
);
INSERT INTO students_changes_log(total_changes) VALUES (0);
Ta muốn tăng giá trị bộ đếm này mỗi khi có thao tác UPDATE trên bảng students.
Hàm để cập nhật bộ đếm:
CREATE OR REPLACE FUNCTION increment_changes_counter()
RETURNS TRIGGER AS $$
BEGIN
UPDATE students_changes_log
SET total_changes = total_changes + 1;
RETURN NULL; -- Trigger cấp thao tác không trả về dòng
END;
$$ LANGUAGE plpgsql;
Tạo trigger FOR EACH STATEMENT:
CREATE TRIGGER update_changes_counter
AFTER UPDATE ON students
FOR EACH STATEMENT
EXECUTE FUNCTION increment_changes_counter();
Test thử:
UPDATE students
SET age = age + 1
WHERE age < 20;
Sau khi chạy lệnh này, trigger sẽ chạy một lần và bộ đếm thay đổi tăng thêm một.
So sánh FOR EACH ROW và FOR EACH STATEMENT
| Tiêu chí | FOR EACH ROW | FOR EACH STATEMENT |
|---|---|---|
| Cấp thực thi | Cho từng dòng bị ảnh hưởng | Một lần cho cả thao tác |
| Tần suất gọi | Một lần cho mỗi dòng | Một lần cho mỗi câu lệnh SQL |
| Nhiệm vụ | Ghi log thay đổi riêng lẻ, xử lý từng dòng | Tổng hợp, cập nhật metadata |
| Ví dụ | Ghi log thay đổi cho từng dòng | Cập nhật bộ đếm thay đổi |
| Hiệu năng | Tốn tài nguyên hơn khi thao tác hàng loạt | Ít tốn tài nguyên hơn cho thao tác hàng loạt |
Khi nào dùng FOR EACH ROW và FOR EACH STATEMENT?
Dùng FOR EACH ROW nếu:
- Bạn muốn trigger chạy cho từng dòng.
- Logic cần gắn với thay đổi của từng dòng cụ thể.
- Bạn cần truy cập dữ liệu
OLDvàNEWcho từng dòng.
Ví dụ: Ghi log thay đổi trong bảng hoặc tự động tạo bản ghi liên quan.
Dùng FOR EACH STATEMENT nếu:
- Bạn muốn thực hiện hành động chỉ một lần cho cả thao tác.
- Logic trigger không phụ thuộc vào từng dòng cụ thể.
- Hiệu năng rất quan trọng và không muốn trigger bị gọi nhiều lần.
Ví dụ: Cập nhật bộ đếm, tính toán metadata cho bảng.
Lỗi thường gặp và lưu ý quan trọng
Dùng đúng loại trigger đôi khi không rõ ràng, nên nhớ những điều sau:
- Một lỗi phổ biến là cố dùng dữ liệu
OLDvàNEWtrong triggerFOR EACH STATEMENT. Điều này sẽ gây lỗi vì hai biến này chỉ có ở trigger cấp dòng. - Trigger cấp dòng (
FOR EACH ROW) có thể làm chậm thao tác nếu câu lệnh ảnh hưởng nhiều dòng. Luôn cân nhắc hiệu năng. - Cẩn thận với khả năng trigger gọi đệ quy. Ví dụ, nếu trigger lại thay đổi dữ liệu trong cùng bảng, có thể gây vòng lặp vô hạn.
GO TO FULL VERSION