Hoewel het antwoord van @Gary technisch correct is, vermeldt hij niet dat PostgreSQL wel ondersteun dit formulier:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Lees de handleiding op UPDATE nog een keer.
Het is nog steeds lastig om zijn gedaan te krijgen met dynamische SQL. Aangezien u niet specificeerde, ga ik uit van een eenvoudig geval waarin weergaven uit dezelfde kolommen bestaan als hun onderliggende tabellen.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Problemen
-
Het speciale record
NEWis niet zichtbaar inEXECUTE. Ik ben geslaagd voorNEWals een enkele parameter met deUSINGclausule vanEXECUTE. -
Zoals besproken,
UPDATEmet lijstvorm vereist individuele waarden . Ik gebruik een subselectie om het record in afzonderlijke kolommen te splitsen:UPDATE ... FROM (SELECT ($1).*) x(haakjes rond
$1zijn niet optioneel.) Hierdoor kan ik eenvoudig twee kolomlijsten gebruiken die zijn gemaakt metstring_agg()uit de catalogustabel:een met en een zonder tafelkwalificatie. -
Het is niet mogelijk om een rijwaarde als geheel toe te wijzen aan afzonderlijke kolommen. De handleiding:
Volgens de standaard kan de bronwaarde voor een tussen haakjes geplaatste sublijst van doelkolomnamen elke expressie met rijwaarde zijn die het juiste aantal kolommen oplevert. PostgreSQL staat alleen toe dat de bronwaarde een rijconstructor of een sub-
SELECTis . -
INSERTeenvoudiger wordt uitgevoerd. Ervan uitgaande dat de structuur van de weergave en de tabel identiek zijn, laat ik de lijst met kolomdefinities weg. (Kan verbeterd worden, zie hieronder.)
Oplossing
Ik heb een aantal updates voor je aanpak gemaakt om het te laten schitteren.
Triggerfunctie voor 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;
Triggerfunctie voor 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;
Triggers:
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 demonstreren INSERT en UPDATE .
Belangrijkste punten
-
Voeg de schemanaam toe om de tabelverwijzing ondubbelzinnig te maken. Er kunnen meerdere instanties van dezelfde tabelnaam in dezelfde database in meerdere schema's voorkomen!
-
Zoekopdracht
pg_attributein plaats vaninformation_schema.columns. Dat is minder draagbaar, maar veel sneller en stelt me in staat om de table-OID te gebruiken.- Hoe te controleren of een tabel in een bepaald schema bestaat
-
Tabelnamen zijn NIET veilig tegen SQLi wanneer behandeld als tekenreeksen, zoals bij het bouwen van query's voor dynamische SQL. Ontsnap met
quote_ident()offormat()of met een object-identifer type. Dit omvat de speciale triggerfunctievariabelenTG_TABLE_SCHEMAenTG_TABLE_NAME! -
Cast naar het object-ID type
regclassom te bevestigen dat de tabelnaam geldig is en de OID op te halen voor het opzoeken van de catalogus. -
Gebruik optioneel
format()om de dynamische queryreeks veilig te bouwen. -
Dynamische SQL is niet nodig voor de eerste query op de catalogustabellen. Sneller, eenvoudiger.
-
Gebruik
RETURN NEWin plaats vanRETURN NULLin deze triggerfuncties, tenzij u weet wat u doet. (NULLzou deINSERT. annuleren voor de huidige rij.) -
Deze eenvoudige versie gaat ervan uit dat elke tabel (en weergave) een unieke kolom heeft met de naam
id. Een meer geavanceerde versie zou de primaire sleutel dynamisch kunnen gebruiken. -
De functie voor
UPDATEstaat toe dat de weergavekolommen en tabel in elke volgorde staan , zolang de set hetzelfde is. De functie voorINSERTverwacht dat de kolommen van weergave en tabel in identieke volgorde staan . Als u willekeurige volgorde wilt toestaan, voegt u een kolomdefinitielijst toe aan deINSERTcommando, net als bijUPDATE. -
Bijgewerkte versie omvat ook wijzigingen aan de
idkolom met behulp vanOLDbovendien.