Även om @Garys svar är tekniskt korrekt, misslyckas han med att nämna att PostgreSQL gör stödja detta formulär:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Läs manualen på UPDATE ännu en gång.
Det är fortfarande svårt att få det gjort med dynamisk SQL. Eftersom du inte specificerade, antar jag ett enkelt fall där vyer består av samma kolumner som deras underliggande tabeller.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Problem
-
Specialposten
NEWär inte synligt iEXECUTE. Jag skickarNEWsom en enda parameter medUSINGsats avEXECUTE. -
Som diskuterats,
UPDATEmed listform behöver individuella värden . Jag använder ett underval för att dela upp posten i enskilda kolumner:UPDATE ... FROM (SELECT ($1).*) x(Parentes runt
$1är inte valfria.) Detta tillåter mig att helt enkelt använda två kolumnlistor byggda medstring_agg()från katalogtabellen:en med och en utan tabellkvalifikation. -
Det är inte möjligt att tilldela ett radvärde som helhet till enskilda kolumner. Manualen:
Enligt standarden kan källvärdet för en underlista i parentes med målkolumnnamn vara vilket radvärderat uttryck som helst som ger korrekt antal kolumner. PostgreSQL tillåter endast att källvärdet är en radkonstruktor eller en sub-
SELECT. -
INSERTimplementeras enklare. Förutsatt att strukturen för vyn och tabellen är identiska utelämnar jag kolumndefinitionslistan. (Kan förbättras, se nedan.)
Lösning
Jag gjorde ett antal uppdateringar av ditt tillvägagångssätt för att få det att lysa.
Triggerfunktion 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;
Triggerfunktion 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;
Utlösare:
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 demonstrerar INSERT och UPDATE .
Huvudpunkter
-
Inkludera schemanamnet för att göra tabellreferensen entydig. Det kan finnas flera instanser av samma tabellnamn i samma databas i flera scheman!
-
Fråga
pg_attributeistället förinformation_schema.columns. Det är mindre bärbart, men mycket snabbare och låter mig använda tabell-OID.- Hur man kontrollerar om en tabell finns i ett givet schema
-
Tabellnamn är INTE säkra mot SQLi när de hanteras som strängar som i att bygga frågor för dynamisk SQL. Escape med
quote_ident()ellerformat()eller med en objektidentifierartyp. Detta inkluderar de speciella triggerfunktionsvariablernaTG_TABLE_SCHEMAochTG_TABLE_NAME! -
Casta till objektidentifieraren typ
regclassför att hävda att tabellnamnet är giltigt och hämta OID för katalogsökningen. -
Använd valfritt
format()för att bygga den dynamiska frågesträngen på ett säkert sätt. -
Inget behov av dynamisk SQL för den första frågan i katalogtabellerna. Snabbare, enklare.
-
Använd
RETURN NEWistället förRETURN NULLi dessa triggerfunktioner om du inte vet vad du gör. (NULLskulle avbrytaINSERTför den aktuella raden.) -
Denna enkla version förutsätter att varje tabell (och vy) har en unik kolumn som heter
id. En mer sofistikerad version kan använda primärnyckeln dynamiskt. -
Funktionen för
UPDATEtillåter vykolumnerna och tabellen att vara i valfri ordning , så länge uppsättningen är densamma. Funktionen förINSERTförväntar sig att vykolumnerna och tabellen är i identisk ordning . Om du vill tillåta godtycklig ordning, lägg till en kolumndefinitionslista tillINSERTkommandot, precis som medUPDATE. -
Uppdaterad version täcker även ändringar av
idkolumnen genom att användaOLDdessutom.