Ini adalah masalah yang rumit. Tapi itu bisa dilakukan dengan pemicu per kolom dan eksekusi pemicu bersyarat yang diperkenalkan di PostgreSQL 9.0 .
Anda memerlukan tanda "diperbarui" per baris untuk solusi ini. Gunakan boolean
kolom dalam tabel yang sama untuk kesederhanaan. Tapi bisa juga di tabel lain atau bahkan tabel sementara per transaksi.
Payload mahal dieksekusi sekali per baris di mana penghitung diperbarui (sekali atau beberapa kali).
Ini juga harus melakukan baik, karena ...
- ... menghindari beberapa panggilan pemicu di root (skala dengan baik)
- ... tidak mengubah baris tambahan (meminimalkan tabel mengasapi)
- ... tidak membutuhkan penanganan exception yang mahal.
Perhatikan hal berikut
Demo
Diuji di PostgreSQL 9.1 dengan skema terpisah x
sebagai lingkungan pengujian.
Tabel dan baris dummy
-- DROP SCHEMA x;
CREATE SCHEMA x;
CREATE TABLE x.tbl (
id int
,counter int
,trig_exec_count integer -- for monitoring payload execution.
,updated bool);
Sisipkan dua baris untuk mendemonstrasikannya berfungsi dengan banyak baris:
INSERT INTO x.tbl VALUES
(1, 0, 0, NULL)
,(2, 0, 0, NULL);
Fungsi dan Pemicu Pemicu
1.) Jalankan muatan mahal
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.) Tandai baris sebagai diperbarui.
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.) Setel ulang tanda "diperbarui".
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;
Nama pemicu relevan! Dipanggil untuk acara yang sama, mereka dieksekusi dalam urutan abjad.
1.) Payload, hanya jika belum "diperbarui":
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.) Tandai baris sebagai diperbarui, hanya jika belum "diperbarui":
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.) Atur Ulang Bendera. Tidak ada pengulangan tanpa akhir karena kondisi pemicu.
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();
Uji
Jalankan UPDATE
&SELECT
secara terpisah untuk melihat efek yang ditangguhkan. Jika dieksekusi bersama (dalam satu transaksi) SELECT akan menampilkan tbl.counter
. yang baru tapi tbl2.trig_exec_count
yang lama .
UPDATE x.tbl SET counter = counter + 1;
SELECT * FROM x.tbl;
Sekarang, perbarui penghitung beberapa kali (dalam satu transaksi). Payload hanya akan dieksekusi sekali. 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;