sql >> Databáze >  >> RDS >> PostgreSQL

Spustit odložený trigger pouze jednou na řádek v PostgreSQL

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;


  1. Jak funguje MYSQL Self-Join?

  2. Index malých písmen Flask-SQLAlchemy – funkce přeskakování, není podporována odrazem SQLAlchemy

  3. java.lang.IllegalArgumentException:sloupec '_id' neexistuje

  4. Blok anonymního kódu PL/pgSQL