Während @Garys Antwort technisch korrekt ist, erwähnt er nicht, dass PostgreSQL dies tut dieses Formular unterstützen:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Lesen Sie das Handbuch unter UPDATE noch einmal.
Es ist immer noch schwierig, mit dynamischem SQL fertig zu werden. Da Sie nichts angegeben haben, gehe ich von einem einfachen Fall aus, in dem Ansichten aus denselben Spalten wie ihre zugrunde liegenden Tabellen bestehen.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Probleme
-
Der spezielle Datensatz
NEWist inEXECUTEnicht sichtbar . Ich übergebeNEWals einzelner Parameter mit demUSINGKlausel vonEXECUTE. -
Wie besprochen,
UPDATEmit list-form benötigt individuelle Werte . Ich verwende eine Unterauswahl, um den Datensatz in einzelne Spalten aufzuteilen:UPDATE ... FROM (SELECT ($1).*) x(Klammern um
$1sind nicht optional.) Dadurch kann ich einfach zwei Spaltenlisten verwenden, die mitstring_agg()erstellt wurden aus der Katalogtabelle:eine mit und eine ohne Tabellenkennzeichnung. -
Es ist nicht möglich, einzelnen Spalten einen Zeilenwert als Ganzes zuzuweisen. Das Handbuch:
Gemäß dem Standard kann der Quellwert für eine eingeklammerte Unterliste von Zielspaltennamen ein beliebiger zeilenwertiger Ausdruck sein, der die richtige Anzahl von Spalten ergibt. PostgreSQL lässt nur zu, dass der Quellwert ein Zeilenkonstruktor oder ein Unter-
SELECTist . -
INSERTist einfacher implementiert. Unter der Annahme, dass die Struktur von Ansicht und Tabelle identisch sind, lasse ich die Spaltendefinitionsliste weg. (Kann verbessert werden, siehe unten.)
Lösung
Ich habe eine Reihe von Aktualisierungen an Ihrem Ansatz vorgenommen, um ihn zum Glänzen zu bringen.
Auslösefunktion für 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;
Auslösefunktion für 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;
Auslöser:
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-Geige Demonstration von INSERT und UPDATE .
Wichtige Punkte
-
Schließen Sie den Schemanamen ein, um die Tabellenreferenz eindeutig zu machen. Es kann mehrere Instanzen desselben Tabellennamens in derselben Datenbank in mehreren Schemas geben!
-
Fragen Sie
pg_attributeab anstelle voninformation_schema.columns. Das ist weniger portabel, aber viel schneller und erlaubt mir, die Tabellen-OID zu verwenden.- Überprüfen, ob eine Tabelle in einem bestimmten Schema existiert
-
Tabellennamen sind NICHT sicher gegen SQLi wenn sie als Zeichenfolgen behandelt werden, wie beim Erstellen von Abfragen für dynamisches SQL. Entkommen Sie mit
quote_ident()oderformat()oder mit einem Objektidentifizierertyp. Dazu gehören die speziellen Trigger-FunktionsvariablenTG_TABLE_SCHEMAundTG_TABLE_NAME! -
In den Objektidentifizierertyp
regclassumwandeln um zu bestätigen, dass der Tabellenname gültig ist, und um die OID für die Katalogsuche zu erhalten. -
Verwenden Sie optional
format()um die dynamische Abfragezeichenfolge sicher zu erstellen. -
Keine Notwendigkeit für dynamisches SQL für die erste Abfrage der Katalogtabellen. Schneller, einfacher.
-
Verwenden Sie
RETURN NEWstattRETURN NULLin diesen Triggerfunktionen, es sei denn, Sie wissen, was Sie tun. (NULLwürdeINSERTabbrechen für die aktuelle Zeile.) -
Diese einfache Version geht davon aus, dass jede Tabelle (und Ansicht) eine eindeutige Spalte namens
idhat . Eine anspruchsvollere Version könnte den Primärschlüssel dynamisch verwenden. -
Die Funktion für
UPDATEermöglicht die beliebige Reihenfolge der Spalten von Ansicht und Tabelle , solange die Menge gleich ist. Die Funktion fürINSERTerwartet, dass die Spalten von Ansicht und Tabelle in identischer Reihenfolge sind . Wenn Sie eine beliebige Reihenfolge zulassen möchten, fügen Sie demINSERTeine Spaltendefinitionsliste hinzu Befehl, genau wie beiUPDATE. -
Die aktualisierte Version deckt auch Änderungen an der
idab Spalte mitOLDzusätzlich.