sql >> Database >  >> RDS >> PostgreSQL

Voer uitgestelde trigger slechts één keer per rij uit in PostgreSQL

Dit is een lastig probleem. Maar het kan worden gedaan met triggers per kolom en voorwaardelijke triggeruitvoering geïntroduceerd in PostgreSQL 9.0 .

Je hebt een "bijgewerkte" vlag per rij . nodig voor deze oplossing. Gebruik een boolean voor de eenvoud in dezelfde tabel. Maar het kan in een andere tafel zijn of zelfs een tijdelijke tafel per transactie.

De dure payload wordt eenmaal per rij . uitgevoerd waar de teller wordt bijgewerkt (eenmalig of meerdere keren).

Dit moet ook presteren wel, omdat ...

  • ... het vermijdt meerdere aanroepen van triggers bij de root (schaalt goed)
  • ... verandert geen extra rijen (minimaliseer de opgeblazenheid van de tabel)
  • ... heeft geen dure exception handling nodig.

Overweeg het volgende

Demo

Getest in PostgreSQL 9.1 met een apart schema x als testomgeving.

Tabellen en dummyrijen

-- DROP SCHEMA x;
CREATE SCHEMA x;

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

Voeg twee rijen in om te laten zien dat het werkt met meerdere rijen:

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

Triggerfuncties en triggers

1.) Voer dure payload uit

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.) Markeer rij als bijgewerkt.

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.) Reset de "bijgewerkte" vlag.

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;

Triggernamen zijn relevant! Opgeroepen voor hetzelfde evenement worden ze in alfabetische volgorde uitgevoerd.

1.) Payload, alleen als deze nog niet "bijgewerkt" is:

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.) Markeer rij als bijgewerkt, alleen als deze nog niet "bijgewerkt" is:

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.) Vlag opnieuw instellen. Geen eindeloze lus vanwege triggerconditie.

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

Voer UPDATE uit &SELECT apart om het uitgestelde effect te zien. Als ze samen worden uitgevoerd (in één transactie), toont de SELECT de nieuwe tbl.counter maar de oude tbl2.trig_exec_count .

UPDATE x.tbl SET counter = counter + 1;

SELECT * FROM x.tbl;

Werk de teller nu meerdere keren bij (in één transactie). De payload wordt maar één keer uitgevoerd. 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. Wat is er nieuw in Access 2016? Is het de moeite waard om te upgraden?

  2. Hoe alleen tijd op te slaan; geen datum en tijd?

  3. PHP 5-applicaties uitvoeren met MySQL 8.0 op CentOS 7

  4. Werken met cPanel MySQL-databases