Ä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 skickarNEW
som en enda parameter medUSING
sats avEXECUTE
. -
Som diskuterats,
UPDATE
med 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
. -
INSERT
implementeras 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_attribute
istä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_SCHEMA
ochTG_TABLE_NAME
! -
Casta till objektidentifieraren typ
regclass
fö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 NEW
istället förRETURN NULL
i dessa triggerfunktioner om du inte vet vad du gör. (NULL
skulle avbrytaINSERT
fö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
UPDATE
tillåter vykolumnerna och tabellen att vara i valfri ordning , så länge uppsättningen är densamma. Funktionen förINSERT
förväntar sig att vykolumnerna och tabellen är i identisk ordning . Om du vill tillåta godtycklig ordning, lägg till en kolumndefinitionslista tillINSERT
kommandot, precis som medUPDATE
. -
Uppdaterad version täcker även ändringar av
id
kolumnen genom att användaOLD
dessutom.