To je zapeklitý problém. Lze to však provést pomocí spouštěčů pro jednotlivé sloupce a podmíněného spouštění spouštěčů zavedených v PostgreSQL 9.0 .
Potřebujete příznak „aktualizováno“ na řádek pro toto řešení. Použijte boolean
sloupec ve stejné tabulce pro zjednodušení. Ale může to být v jiné tabulce nebo dokonce v dočasné tabulce na transakci.
Drahé užitečné zatížení se provádí jednou na řádek kde je počítadlo aktualizováno (jednou nebo vícekrát).
To by také mělo provést no, protože ...
- ...vyhýbá se vícenásobným voláním spouštěčů v kořenovém adresáři (dobře se škáluje)
- ... nemění další řádky (minimalizuje nadýmání tabulky)
- ... nepotřebuje drahé zpracování výjimek.
Zvažte následující
Ukázka
Testováno v PostgreSQL 9.1 se samostatným schématem x
jako testovací prostředí.
Tabulky a fiktivní řádky
-- DROP SCHEMA x;
CREATE SCHEMA x;
CREATE TABLE x.tbl (
id int
,counter int
,trig_exec_count integer -- for monitoring payload execution.
,updated bool);
Vložte dva řádky, abyste demonstrovali, že funguje s více řádky:
INSERT INTO x.tbl VALUES
(1, 0, 0, NULL)
,(2, 0, 0, NULL);
Spouštěcí funkce a spouštěče
1.) Proveďte drahé užitečné zatížení
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.) Označte řádek jako aktualizovaný.
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.) Resetujte příznak „aktualizováno“.
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;
Názvy spouštěčů jsou relevantní! Vyvolány pro stejnou událost jsou provedeny v abecedním pořadí.
1.) Užitečné zatížení, pouze pokud ještě není "aktualizováno":
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.) Označte řádek jako aktualizovaný, pouze pokud ještě není "aktualizovaný":
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.) Resetovat příznak. Žádná nekonečná smyčka kvůli spouštěcí podmínce.
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();
Test
Spusťte UPDATE
&SELECT
samostatně, abyste viděli odložený účinek. Při společném provedení (v jedné transakci) SELECT zobrazí nový tbl.counter
ale starý tbl2.trig_exec_count
.
UPDATE x.tbl SET counter = counter + 1;
SELECT * FROM x.tbl;
Nyní aktualizujte počítadlo vícekrát (v jedné transakci). Užitečné zatížení bude provedeno pouze jednou. 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;