sql >> Database teknologi >  >> RDS >> PostgreSQL

Udfør kun udskudt trigger én gang pr. række i PostgreSQL

Dette er et vanskeligt problem. Men det kan gøres med per-kolonne-triggere og betinget trigger-udførelse introduceret i PostgreSQL 9.0 .

Du skal bruge et "opdateret" flag pr. række for denne løsning. Brug en boolean kolonne i samme tabel for nemheds skyld. Men det kunne være i en anden tabel eller endda en midlertidig tabel pr. transaktion.

Den dyre nyttelast udføres én gang pr. række hvor tælleren opdateres (en gang eller flere gange).

Dette bør også virke godt, fordi ...

  • ... det undgår flere kald af triggere ved roden (skalerer godt)
  • ... ændrer ikke yderligere rækker (minimer tabelbloat)
  • ... kræver ikke dyr undtagelseshåndtering.

Overvej følgende

Demo

Testet i PostgreSQL 9.1 med et separat skema x som testmiljø.

Tabeller og dummy-rækker

-- DROP SCHEMA x;
CREATE SCHEMA x;

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

Indsæt to rækker for at demonstrere, at det fungerer med flere rækker:

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

Triggerfunktioner og triggere

1.) Udfør dyr nyttelast

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.) Markér række som opdateret.

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.) Nulstil "opdateret" flag.

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;

Triggernavne er relevante! Kaldet til den samme begivenhed udføres de i alfabetisk rækkefølge.

1.) Nyttelast, kun hvis den ikke er "opdateret" endnu:

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.) Marker rækken som opdateret, kun hvis den ikke er "opdateret" endnu:

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.) Nulstil flag. Ingen endeløs loop på grund af triggertilstand.

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

Kør UPDATE &SELECT separat for at se den udskudte effekt. Hvis det udføres sammen (i én transaktion), vil SELECT vise den nye tbl.counter men den gamle tbl2.trig_exec_count .

UPDATE x.tbl SET counter = counter + 1;

SELECT * FROM x.tbl;

Opdater nu tælleren flere gange (i én transaktion). Nyttelasten vil kun blive udført én gang. 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. Sådan administreres sessioner i Node.js ved hjælp af Passport, Redis og MySQL

  2. OLTP-ydelse siden PostgreSQL 8.3

  3. Kører MySQL *.sql-filer i PHP

  4. Er mysql_real_escape_string() brudt?