Sebbene la risposta di @Gary sia tecnicamente corretta, non menziona che PostgreSQL lo fa supporta questo modulo:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Leggi il manuale su UPDATE
ancora una volta.
È ancora difficile farlo con SQL dinamico. Dal momento che non hai specificato, presumo un caso semplice in cui le viste sono costituite dalle stesse colonne delle tabelle sottostanti.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Problemi
-
Il record speciale
NEW
non è visibile all'interno diEXECUTE
. PassoNEW
come parametro singolo conUSING
clausola diEXECUTE
. -
Come discusso,
UPDATE
con list-form ha bisogno di valori individuali . Uso una sottoselezione per dividere il record in singole colonne:UPDATE ... FROM (SELECT ($1).*) x
(Tra parentesi intorno a
$1
non sono opzionali.) Questo mi permette di usare semplicemente due elenchi di colonne costruiti constring_agg()
dalla tabella di catalogo:una con e una senza qualifica di tabella. -
Non è possibile assegnare un valore di riga nel suo insieme a singole colonne. Il manuale:
Secondo lo standard, il valore di origine per un sottoelenco tra parentesi di nomi di colonne di destinazione può essere qualsiasi espressione con valori di riga che fornisca il numero corretto di colonne. PostgreSQL consente solo al valore di origine di essere un costruttore di riga o un sotto-
SELECT
. -
INSERT
è implementato in modo più semplice. Supponendo che la struttura della vista e della tabella siano identiche, ometto l'elenco delle definizioni delle colonne. (Può essere migliorato, vedi sotto.)
Soluzione
Ho apportato una serie di aggiornamenti al tuo approccio per farlo brillare.
Funzione di attivazione per 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;
Funzione di trigger per 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;
Trigger:
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 dimostrando INSERT
e UPDATE
.
Punti principali
-
Includere il nome dello schema per rendere univoco il riferimento alla tabella. Possono esserci più istanze dello stesso nome di tabella nello stesso database in più schemi!
-
Interroga
pg_attribute
invece diinformation_schema.columns
. È meno portatile, ma molto più veloce e mi permette di usare il table-OID.- Come verificare se esiste una tabella in un determinato schema
-
I nomi delle tabelle NON sono sicuri contro SQLi se gestiti come stringhe come nella creazione di query per SQL dinamico. Esci con
quote_ident()
oformat()
o con un tipo di identificatore di oggetto. Ciò include le variabili della funzione trigger specialeTG_TABLE_SCHEMA
eTG_TABLE_NAME
! -
Trasmetti al tipo di identificatore oggetto
regclass
per affermare che il nome della tabella è valido e ottenere l'OID per la ricerca nel catalogo. -
Facoltativamente usa
format()
per creare la stringa di query dinamica in modo sicuro. -
Non c'è bisogno di SQL dinamico per la prima query sulle tabelle del catalogo. Più veloce, più semplice.
-
Usa
RETURN NEW
invece diRETURN NULL
in queste funzioni di attivazione a meno che tu non sappia cosa stai facendo. (NULL
cancellerebbe ilINSERT
per la riga corrente.) -
Questa versione semplice presuppone che ogni tabella (e vista) abbia una colonna univoca denominata
id
. Una versione più sofisticata potrebbe utilizzare la chiave primaria in modo dinamico. -
La funzione per
UPDATE
consente alle colonne di visualizzazione e tabella di essere in qualsiasi ordine , purché il set sia lo stesso. La funzione perINSERT
prevede che le colonne della vista e della tabella siano in ordine identico . Se vuoi consentire un ordine arbitrario, aggiungi un elenco di definizioni di colonna aINSERT
comando, proprio come conUPDATE
. -
La versione aggiornata copre anche le modifiche all'
id
colonna utilizzandoOLD
in aggiunta.