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
NEW
ist inEXECUTE
nicht sichtbar . Ich übergebeNEW
als einzelner Parameter mit demUSING
Klausel vonEXECUTE
. -
Wie besprochen,
UPDATE
mit list-form benötigt individuelle Werte . Ich verwende eine Unterauswahl, um den Datensatz in einzelne Spalten aufzuteilen:UPDATE ... FROM (SELECT ($1).*) x
(Klammern um
$1
sind 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-
SELECT
ist . -
INSERT
ist 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_attribute
ab 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_SCHEMA
undTG_TABLE_NAME
! -
In den Objektidentifizierertyp
regclass
umwandeln 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 NEW
stattRETURN NULL
in diesen Triggerfunktionen, es sei denn, Sie wissen, was Sie tun. (NULL
würdeINSERT
abbrechen für die aktuelle Zeile.) -
Diese einfache Version geht davon aus, dass jede Tabelle (und Ansicht) eine eindeutige Spalte namens
id
hat . Eine anspruchsvollere Version könnte den Primärschlüssel dynamisch verwenden. -
Die Funktion für
UPDATE
ermöglicht die beliebige Reihenfolge der Spalten von Ansicht und Tabelle , solange die Menge gleich ist. Die Funktion fürINSERT
erwartet, dass die Spalten von Ansicht und Tabelle in identischer Reihenfolge sind . Wenn Sie eine beliebige Reihenfolge zulassen möchten, fügen Sie demINSERT
eine Spaltendefinitionsliste hinzu Befehl, genau wie beiUPDATE
. -
Die aktualisierte Version deckt auch Änderungen an der
id
ab Spalte mitOLD
zusätzlich.