sql >> Databasteknik >  >> RDS >> PostgreSQL

Kör uppskjuten trigger endast en gång per rad i PostgreSQL

Det här är ett knepigt problem. Men det kan göras med utlösare per kolumn och exekvering av villkorad utlösare som introduceras i PostgreSQL 9.0 .

Du behöver en "uppdaterad" flagga per rad för denna lösning. Använd en boolean kolumn i samma tabell för enkelhets skull. Men det kan vara i en annan tabell eller till och med en tillfällig tabell per transaktion.

Den dyra nyttolasten exekveras en gång per rad där räknaren uppdateras (en eller flera gånger).

Detta bör också prestera ja, för att ...

  • ... det undviker flera anrop av triggers vid roten (skalar väl)
  • ... ändrar inte ytterligare rader (minimera tabelluppsvällning)
  • ... kräver ingen dyr undantagshantering.

Tänk på följande

Demo

Testad i PostgreSQL 9.1 med ett separat schema x som testmiljö.

Tabell och dummyrader

-- DROP SCHEMA x;
CREATE SCHEMA x;

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

Infoga två rader för att visa att det fungerar med flera rader:

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

Triggerfunktioner och triggers

1.) Utför dyr nyttolast

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.) Flagga raden som uppdaterad.

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.) Återställ "uppdaterad" flagga.

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;

Triggernamn är relevanta! Kallas för samma händelse de körs i alfabetisk ordning.

1.) Nyttolast, endast om den inte är "uppdaterad" ännu:

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.) Flagga raden som uppdaterad, bara om den inte är "uppdaterad" ännu:

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.) Återställ flagga. Ingen ändlös loop på grund av triggertillstånd.

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();

Testa

Kör UPDATE &SELECT separat för att se den uppskjutna effekten. Om den körs tillsammans (i en transaktion) kommer SELECT att visa den nya tbl.counter men den gamla tbl2.trig_exec_count .

UPDATE x.tbl SET counter = counter + 1;

SELECT * FROM x.tbl;

Uppdatera nu räknaren flera gånger (i en transaktion). Nyttolasten kommer bara att exekveras en gång. 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. Öka automatiskt i Oracle utan att använda en trigger

  2. Hur anger jag en unik begränsning för flera kolumner i MySQL?

  3. Använd Firebase DB med lokal DB

  4. oci_connect-anslutningen misslyckades