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
NEW
is niet zichtbaar inEXECUTE
. Ik ben geslaagd voorNEW
als een enkele parameter met deUSING
clausule vanEXECUTE
. -
Zoals besproken,
UPDATE
met lijstvorm vereist individuele waarden . Ik gebruik een subselectie om het record in afzonderlijke kolommen te splitsen:UPDATE ... FROM (SELECT ($1).*) x
(haakjes rond
$1
zijn 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-
SELECT
is . -
INSERT
eenvoudiger 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_attribute
in 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_SCHEMA
enTG_TABLE_NAME
! -
Cast naar het object-ID type
regclass
om 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 NEW
in plaats vanRETURN NULL
in deze triggerfuncties, tenzij u weet wat u doet. (NULL
zou 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
UPDATE
staat toe dat de weergavekolommen en tabel in elke volgorde staan , zolang de set hetzelfde is. De functie voorINSERT
verwacht dat de kolommen van weergave en tabel in identieke volgorde staan . Als u willekeurige volgorde wilt toestaan, voegt u een kolomdefinitielijst toe aan deINSERT
commando, net als bijUPDATE
. -
Bijgewerkte versie omvat ook wijzigingen aan de
id
kolom met behulp vanOLD
bovendien.