これは難しい問題です。ただし、 PostgreSQL 9.0で導入された列ごとのトリガーと条件付きトリガーの実行を使用して実行できます。 。
行ごとに「更新された」フラグが必要です このソリューションのために。 boolean
を使用する 簡単にするために、同じテーブルの列。ただし、別のテーブルにある場合もあれば、トランザクションごとの一時テーブルにある場合もあります。
高価なペイロードは行ごとに1回実行されます カウンターが更新される場所(1回または複数回)。
これも実行する必要があります まあ、なぜなら...
- ...ルートでのトリガーの複数の呼び出しを回避します(適切にスケーリングします)
- ...追加の行を変更しません(テーブルの肥大化を最小限に抑えます)
- ...高価な例外処理は必要ありません。
次のことを考慮してください
デモ
別のスキーマx
を使用してPostgreSQL9.1でテスト済み テスト環境として。
テーブルとダミー行
-- DROP SCHEMA x;
CREATE SCHEMA x;
CREATE TABLE x.tbl (
id int
,counter int
,trig_exec_count integer -- for monitoring payload execution.
,updated bool);
2つの行を挿入して、複数の行で機能することを示します。
INSERT INTO x.tbl VALUES
(1, 0, 0, NULL)
,(2, 0, 0, NULL);
トリガー機能とトリガー
1.)高価なペイロードを実行する
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.)行に更新済みのフラグを付けます。
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。)「更新済み」フラグをリセットします。
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;
トリガー名は関連性があります!同じイベントのために呼び出され、アルファベット順に実行されます。
1.)ペイロード、まだ「更新」されていない場合のみ:
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.)まだ「更新」されていない場合にのみ、行に更新済みのフラグを付けます:
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.)フラグをリセットします。トリガー条件による無限のループはありません。
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();
テスト
UPDATE
を実行します &SELECT
延期された効果を確認するために個別に。 (1つのトランザクションで)一緒に実行された場合、SELECTは新しいtbl.counter
を表示します しかし、古いtbl2.trig_exec_count
。
UPDATE x.tbl SET counter = counter + 1;
SELECT * FROM x.tbl;
ここで、カウンターを複数回更新します(1つのトランザクションで)。ペイロードは1回だけ実行されます。 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;