Giả sử bạn đang code một app quản lý sinh viên và khóa học, và bạn có bảng students. Trong bảng này có trường last_modified, trường này cần tự động cập nhật mỗi khi có thay đổi dữ liệu trong bản ghi (ví dụ đổi tên sinh viên hoặc tuổi của họ).
Thay vì phải tự tay viết update last_modified trong từng câu lệnh SQL, mình sẽ tạo một trigger để nó tự làm cho mình.
Cấu trúc bảng students
Đầu tiên, mình sẽ tạo bảng students để dùng làm ví dụ. Bảng này chứa info cơ bản về sinh viên:
CREATE TABLE students (
student_id SERIAL PRIMARY KEY, -- Định danh duy nhất của sinh viên
name VARCHAR(100) NOT NULL, -- Tên sinh viên
age INT, -- Tuổi sinh viên
last_modified TIMESTAMP NOT NULL DEFAULT NOW() -- Thời gian thay đổi gần nhất
);
- Trường
last_modifiedban đầu sẽ được set bằng thời gian hiện tại (NOW()) khi tạo bản ghi. - Trường này sẽ tự động cập nhật khi dữ liệu sinh viên thay đổi.
Giờ mình sẽ insert một số dữ liệu test vào bảng:
INSERT INTO students (name, age)
VALUES
('Otto Lin', 20),
('Maria Chi', 22),
('Alex Song', 19);
Bây giờ dữ liệu trong bảng sẽ như sau:
| 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 | Alex Song | 19 | 2023-10-15 12:00:00 |
Tạo function để cập nhật last_modified
Function bằng PL/pgSQL sẽ được trigger gọi để update giá trị trường last_modified. Nó sẽ tự động chạy trước khi update bản ghi.
Tạo function update_last_modified nhé:
CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
-- Cập nhật trường last_modified thành thời gian hiện tại
NEW.last_modified := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
NEW— là biến đặc biệt chứa dữ liệu mới của bản ghi (sau khi thay đổi).- Mình set
NEW.last_modifiedbằngNOW()(ngày giờ hiện tại). - Function trả về biến
NEWđã được cập nhật, cần thiết để trigger hoạt động đúng.
Tạo trigger
Giờ mình sẽ tạo trigger để tự động gọi function update_last_modified mỗi khi update bản ghi trong bảng students.
CREATE TRIGGER set_last_modified
BEFORE UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();
Ở đây có gì:
BEFORE UPDATEnghĩa là trigger chạy trước khi thực hiện update.FOR EACH ROWnghĩa là trigger chạy cho từng dòng bị thay đổi.EXECUTE FUNCTION update_last_modified()là gọi functionupdate_last_modified.
Test trigger
Giờ mình thử xem trigger chạy thế nào nhé. Lấy dữ liệu hiện tại từ bảng students:
SELECT * FROM students;
Kết quả:
| 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 | Alex Song | 19 | 2023-10-15 12:00:00 |
Giờ mình update tuổi của sinh viên có student_id = 1:
UPDATE students
SET age = 21
WHERE student_id = 1;
Lấy lại dữ liệu từ bảng:
SELECT * FROM students;
Kết quả mong đợi:
| 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 | Alex Song | 19 | 2023-10-15 12:00:00 |
Lưu ý: trường last_modified của bản ghi có student_id = 1 đã được cập nhật thành thời gian hiện tại, còn các bản ghi khác thì không đổi.
Mở rộng logic trigger
Giả sử giờ bạn muốn trường last_modified chỉ cập nhật khi thay đổi một số cột nhất định. Ví dụ, chỉ khi tên sinh viên hoặc tuổi thay đổi thì trigger mới chạy, còn thay đổi khác thì không.
Để làm vậy, bạn có thể thêm điều kiện với toán tử WHEN trong định nghĩa trigger.
Tạo trigger mới có điều kiện nhé:
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();
Ở đây:
- Điều kiện
WHENkiểm tra xem giá trị cũ (OLD) có khác giá trị mới (NEW) ở cộtnamehoặcagekhông. - Nếu không có cột nào trong hai cột này thay đổi thì trigger sẽ không chạy.
Thử update lại dữ liệu trong bảng và test logic mới nhé.
Tips khi dùng trigger
- Đừng lạm dụng trigger. Dùng thì tiện thật, nhưng có thể làm logic database phức tạp và khó debug hơn.
- Luôn ghi chú rõ trigger dùng để làm gì, cho trường hợp nào.
- Dùng điều kiện
WHENđể hạn chế trigger chạy lung tung không cần thiết. - Nhớ là trigger có thể ảnh hưởng đến hiệu năng database, nhất là khi bảng có nhiều bản ghi.
Lỗi phổ biến khi làm việc với trigger
Thay đổi dữ liệu sai. Ví dụ, quên set giá trị cho NEW và trả về dữ liệu gốc không thay đổi gì.
Điều kiện sai. Ví dụ, quên thêm điều kiện WHEN, trigger chạy cả khi không cần update gì.
Đệ quy. Nếu trigger gọi function mà function lại gọi trigger, có thể tạo vòng lặp vô hạn. PostgreSQL có bảo vệ chống đệ quy, nhưng tốt nhất là tránh trường hợp này.
Ví dụ này cho thấy dùng trigger có thể giúp tự động hóa việc cập nhật dữ liệu rất tiện. Trong thực tế, kỹ thuật này thường dùng để log thay đổi, đảm bảo toàn vẹn dữ liệu và tự động hóa các thao tác lặp đi lặp lại.
GO TO FULL VERSION