PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Wykonaj odroczony wyzwalacz tylko raz na wiersz w PostgreSQL

To trudny problem. Ale można to zrobić za pomocą wyzwalaczy dla poszczególnych kolumn i warunkowego wykonywania wyzwalaczy wprowadzonych w PostgreSQL 9.0 .

Potrzebujesz flagi „zaktualizowanej” na wiersz dla tego rozwiązania. Użyj boolean kolumna w tej samej tabeli dla uproszczenia. Ale może to być w innej tabeli lub nawet w tabeli tymczasowej na transakcję.

Kosztowny ładunek jest wykonywany raz na wiersz gdzie licznik jest aktualizowany (raz lub wiele razy).

Powinno to również działać no bo...

  • ... pozwala uniknąć wielu wywołań wyzwalaczy u źródła (dobrze skaluje się)
  • ... nie zmienia dodatkowych wierszy (minimalizuje rozdęcie tabeli)
  • ... nie wymaga kosztownej obsługi wyjątków.

Rozważ następujące

Demo

Testowane w PostgreSQL 9.1 z osobnym schematem x jako środowisko testowe.

Tabele i atrapy wierszy

-- DROP SCHEMA x;
CREATE SCHEMA x;

CREATE TABLE x.tbl (
 id int
,counter int
,trig_exec_count integer  -- for monitoring payload execution.
,updated bool);

Wstaw dwa wiersze, aby zademonstrować, że działa z wieloma wierszami:

INSERT INTO x.tbl VALUES
 (1, 0, 0, NULL)
,(2, 0, 0, NULL);

Funkcje i wyzwalacze

1.) Wykonuj drogi ładunek

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.) Oznacz wiersz jako zaktualizowany.

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.) Zresetuj flagę „zaktualizowane”.

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;

Nazwy wyzwalaczy są istotne! Wywoływane dla tego samego zdarzenia, są wykonywane w kolejności alfabetycznej.

1.) Ładunek, tylko jeśli nie został jeszcze „zaktualizowany”:

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.) Oznacz wiersz jako zaktualizowany, tylko jeśli nie jest jeszcze „zaktualizowany”:

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.) Zresetuj flagę. Brak nieskończonej pętli z powodu warunku wyzwalania.

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

Uruchom UPDATE &SELECT osobno, aby zobaczyć odroczony efekt. Jeśli zostaną wykonane razem (w jednej transakcji), SELECT pokaże nowy tbl.counter ale stary tbl2.trig_exec_count .

UPDATE x.tbl SET counter = counter + 1;

SELECT * FROM x.tbl;

Teraz zaktualizuj licznik kilka razy (w jednej transakcji). Ładunek zostanie wykonany tylko raz. Witajcie!

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Najlepszy sposób na losowe wybieranie wierszy PostgreSQL

  2. Wymień wszystkie sekwencje w Postgres db 8.1 z SQL

  3. Postgres:sprawdź, czy pole tablicy zawiera wartość?

  4. Blok anonimowego kodu PL/pgSQL

  5. Konwertuj dzień juliański na datę w PostgreSQL