Đây là một vấn đề khó. Nhưng nó có thể được thực hiện với trình kích hoạt mỗi cột và thực thi trình kích hoạt có điều kiện được giới thiệu trong PostgreSQL 9.0 .
Bạn cần cờ "cập nhật" trên mỗi hàng cho giải pháp này. Sử dụng boolean
cột trong cùng một bảng cho đơn giản. Nhưng nó có thể nằm trong một bảng khác hoặc thậm chí là một bảng tạm thời cho mỗi giao dịch.
Tải trọng đắt tiền được thực thi một lần trên mỗi hàng nơi cập nhật bộ đếm (một lần hoặc nhiều lần).
Điều này cũng sẽ thực hiện tốt, bởi vì ...
- ... nó tránh nhiều lệnh gọi của trình kích hoạt ở gốc (tỷ lệ tốt)
- ... không làm thay đổi các hàng bổ sung (giảm thiểu sự cồng kềnh của bảng)
- ... không cần xử lý ngoại lệ tốn kém.
Hãy xem xét những điều sau
Bản trình diễn
Đã kiểm tra trong PostgreSQL 9.1 với một lược đồ riêng biệt x
làm môi trường thử nghiệm.
Bảng và hàng giả
-- DROP SCHEMA x;
CREATE SCHEMA x;
CREATE TABLE x.tbl (
id int
,counter int
,trig_exec_count integer -- for monitoring payload execution.
,updated bool);
Chèn hai hàng để chứng minh nó hoạt động với nhiều hàng:
INSERT INTO x.tbl VALUES
(1, 0, 0, NULL)
,(2, 0, 0, NULL);
Chức năng trình kích hoạt và Trình kích hoạt
1.) Thực hiện tải trọng đắt tiền
CREATE OR REPLACE FUNCTION x.trg_upaft_counter_change_1()
RETURNS trigger AS
$BODY$
BEGIN
-- PERFORM some_expensive_procedure(NEW.id);
-- Update trig_exec_count to count execution of expensive payload.
-- Could be in another table, for simplicity, I use the same:
UPDATE x.tbl t
SET trig_exec_count = trig_exec_count + 1
WHERE t.id = NEW.id;
RETURN NULL; -- RETURN value of AFTER trigger is ignored anyway
END;
$BODY$ LANGUAGE plpgsql;
2.) Gắn cờ hàng được cập nhật.
CREATE OR REPLACE FUNCTION x.trg_upaft_counter_change_2()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE x.tbl
SET updated = TRUE
WHERE id = NEW.id;
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
3.) Đặt lại cờ "đã cập nhật".
CREATE OR REPLACE FUNCTION x.trg_upaft_counter_change_3()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE x.tbl
SET updated = NULL
WHERE id = NEW.id;
RETURN NULL;
END;
$BODY$ LANGUAGE plpgsql;
Tên kích hoạt có liên quan! Được gọi cho cùng một sự kiện, chúng được thực thi theo thứ tự bảng chữ cái.
1.) Tải trọng, chỉ khi chưa được "cập nhật":
CREATE CONSTRAINT TRIGGER upaft_counter_change_1
AFTER UPDATE OF counter ON x.tbl
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
WHEN (NEW.updated IS NULL)
EXECUTE PROCEDURE x.trg_upaft_counter_change_1();
2.) Gắn cờ hàng là đã cập nhật, chỉ khi chưa được "cập nhật":
CREATE TRIGGER upaft_counter_change_2 -- not deferred!
AFTER UPDATE OF counter ON x.tbl
FOR EACH ROW
WHEN (NEW.updated IS NULL)
EXECUTE PROCEDURE x.trg_upaft_counter_change_2();
3.) Đặt lại cờ. Không có vòng lặp vô tận vì điều kiện kích hoạt.
CREATE CONSTRAINT TRIGGER upaft_counter_change_3
AFTER UPDATE OF updated ON x.tbl
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
WHEN (NEW.updated) --
EXECUTE PROCEDURE x.trg_upaft_counter_change_3();
Kiểm tra
Chạy UPDATE
&SELECT
riêng biệt để xem hiệu ứng hoãn lại. Nếu được thực hiện cùng nhau (trong một giao dịch), SELECT sẽ hiển thị tbl.counter
mới nhưng tbl2.trig_exec_count
cũ .
UPDATE x.tbl SET counter = counter + 1;
SELECT * FROM x.tbl;
Bây giờ, hãy cập nhật bộ đếm nhiều lần (trong một lần giao dịch). Tải trọng sẽ chỉ được thực hiện một lần. Voilá!
UPDATE x.tbl SET counter = counter + 1;
UPDATE x.tbl SET counter = counter + 1;
UPDATE x.tbl SET counter = counter + 1;
UPDATE x.tbl SET counter = counter + 1;
UPDATE x.tbl SET counter = counter + 1;
SELECT * FROM x.tbl;