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

Opdater flere kolonner i en triggerfunktion i plpgsql

Selvom @Garys svar er teknisk korrekt, undlader han at nævne, at PostgreSQL gør understøtte denne formular:

UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)

Læs manualen på UPDATE endnu en gang.

Det er stadig svært at få det gjort med dynamisk SQL. Da du ikke specificerede, antager jeg et simpelt tilfælde, hvor visninger består af de samme kolonner som deres underliggende tabeller.

CREATE VIEW tbl_view AS SELECT * FROM tbl;

Problemer

  • Den særlige post NEW er ikke synlig i EXECUTE . Jeg sender NEW som en enkelt parameter med USING klausul af EXECUTE .

  • Som diskuteret, UPDATE med listeform kræver individuelle værdier . Jeg bruger et undervalg til at opdele posten i individuelle kolonner:

    UPDATE ...
    FROM  (SELECT ($1).*) x
    

    (Parentes omkring $1 er ikke valgfri.) Dette giver mig mulighed for blot at bruge to kolonnelister bygget med string_agg() fra katalogtabellen:en med og en uden tabelkvalifikation.

  • Det er ikke muligt at tildele en rækkeværdi som helhed til individuelle kolonner. Manualen:

    Ifølge standarden kan kildeværdien for en underliste i parentes med målkolonnenavne være et hvilket som helst udtryk med rækkeværdi, der giver det korrekte antal kolonner. PostgreSQL tillader kun, at kildeværdien er en rækkekonstruktør eller en under-SELECT .

  • INSERT implementeres enklere. Forudsat at strukturen af ​​visningen og tabellen er identiske, udelader jeg kolonnedefinitionslisten. (Kan forbedres, se nedenfor.)

Løsning

Jeg har lavet en række opdateringer til din tilgang for at få den til at skinne.

Triggerfunktion for UPDATE :

CREATE OR REPLACE FUNCTION f_trg_up()
  RETURNS TRIGGER AS
$func$
DECLARE
   tbl  text := quote_ident(TG_TABLE_SCHEMA) || '.'
             || quote_ident(substring(TG_TABLE_NAME from '(.+)_view$'));
   cols text;
   vals text;
BEGIN
   SELECT INTO cols, vals
          string_agg(quote_ident(attname), ', ')
         ,string_agg('x.' || quote_ident(attname), ', ')
   FROM   pg_attribute
   WHERE  attrelid = tbl::regclass
   AND    NOT attisdropped   -- no dropped (dead) columns
   AND    attnum > 0;        -- no system columns

   EXECUTE format('
   UPDATE %s t
   SET   (%s) = (%s)
   FROM  (SELECT ($1).*) x
   WHERE  t.id = ($2).id'
   , tbl, cols, vals) -- assuming unique "id" in every table
   USING NEW, OLD;

   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

Triggerfunktion for INSERT :

CREATE OR REPLACE FUNCTION f_trg_ins()
  RETURNS TRIGGER AS
$func$
DECLARE
    tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
             || quote_ident(substring(TG_TABLE_NAME from '(.+)_view$'));
BEGIN
   EXECUTE 'INSERT INTO ' || tbl || ' SELECT ($1).*'
   USING NEW;

   RETURN NEW;  -- don't return NULL unless you know what you're doing
END
$func$ LANGUAGE plpgsql;

Udløsere:

CREATE TRIGGER trg_instead_up
INSTEAD OF UPDATE ON a_view
FOR EACH ROW EXECUTE PROCEDURE f_trg_up();

CREATE TRIGGER trg_instead_ins
INSTEAD OF INSERT ON a_view
FOR EACH ROW EXECUTE PROCEDURE f_trg_ins();

SQL Fiddle demonstrerer INSERT og UPDATE .

Vigtige punkter

  • Inkluder skemanavnet for at gøre tabelreferencen utvetydig. Der kan være flere forekomster af det samme tabelnavn i den samme database i flere skemaer!

  • Forespørgsel pg_attribute i stedet for information_schema.columns . Det er mindre bærbart, men meget hurtigere og giver mig mulighed for at bruge table-OID.

    • Sådan kontrollerer du, om en tabel findes i et givet skema
  • Tabelnavne er IKKE sikre mod SQLi når det håndteres som strenge som i opbygning af forespørgsler til dynamisk SQL. Escape med quote_ident() eller format() eller med en objektidentifikatortype. Dette inkluderer de specielle triggerfunktionsvariabler TG_TABLE_SCHEMA og TG_TABLE_NAME !

  • Cast til objektidentifikatortypen regclass for at hævde, at tabelnavnet er gyldigt og få OID'et til katalogopslag.

  • Brug eventuelt format() for at bygge den dynamiske forespørgselsstreng sikkert.

  • Intet behov for dynamisk SQL til den første forespørgsel på katalogtabellerne. Hurtigere, enklere.

  • Brug RETURN NEW i stedet for RETURN NULL i disse triggerfunktioner, medmindre du ved, hvad du laver. (NULL ville annullere INSERT for den aktuelle række.)

  • Denne simple version antager, at hver tabel (og visning) har en unik kolonne ved navn id . En mere sofistikeret version kan bruge den primære nøgle dynamisk.

  • Funktionen til UPDATE tillader visningskolonner og tabel at være i vilkårlig rækkefølge , så længe sættet er det samme. Funktionen for INSERT forventer, at kolonnerne med visning og tabel er i identisk rækkefølge . Hvis du vil tillade vilkårlig rækkefølge, skal du tilføje en kolonnedefinitionsliste til INSERT kommando, ligesom med UPDATE .

  • Opdateret version dækker også ændringer af id kolonne ved at bruge OLD desuden.



  1. Tæller antallet af sammenføjede rækker i venstre sammenføjning

  2. liste data til brugbart format?

  3. GWFG i Oracle RAC

  4. SQLite vælg rækker, hvis tidsstemplet matcher dagens dato