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 iEXECUTE
. Jeg senderNEW
som en enkelt parameter medUSING
klausul afEXECUTE
. -
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 medstring_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 forinformation_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()
ellerformat()
eller med en objektidentifikatortype. Dette inkluderer de specielle triggerfunktionsvariablerTG_TABLE_SCHEMA
ogTG_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 forRETURN NULL
i disse triggerfunktioner, medmindre du ved, hvad du laver. (NULL
ville annullereINSERT
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 forINSERT
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 tilINSERT
kommando, ligesom medUPDATE
. -
Opdateret version dækker også ændringer af
id
kolonne ved at brugeOLD
desuden.