Zatímco odpověď @Garyho je technicky správná, nezmiňuje, že PostgreSQL ano podpořte tento formulář:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Přečtěte si příručku na UPDATE
ještě jednou.
Je stále obtížné ho provést pomocí dynamického SQL. Protože jste to nespecifikovali, předpokládám jednoduchý případ, kdy se pohledy skládají ze stejných sloupců jako jejich podkladové tabulky.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Problémy
-
Speciální záznam
NEW
není vidět vEXECUTE
. Prošel jsemNEW
jako jeden parametr sUSING
klauzuleEXECUTE
. -
Jak bylo uvedeno,
UPDATE
se seznamem potřebuje individuální hodnoty . K rozdělení záznamu do jednotlivých sloupců používám subselect:UPDATE ... FROM (SELECT ($1).*) x
(Závorka kolem
$1
nejsou volitelné.) To mi umožňuje jednoduše použít dva seznamy sloupců vytvořené pomocístring_agg()
z katalogové tabulky:jeden s a jeden bez kvalifikace tabulky. -
Není možné přiřadit hodnotu řádku jako celek k jednotlivým sloupcům. Manuál:
Podle standardu může být zdrojovou hodnotou pro dílčí seznam názvů cílových sloupců v závorkách jakýkoli výraz s hodnotou řádku poskytující správný počet sloupců. PostgreSQL povoluje, aby zdrojovou hodnotou byl pouze konstruktor řádku nebo podřazený
SELECT
. -
INSERT
je implementován jednodušší. Za předpokladu, že struktura pohledu a tabulky jsou totožné, vynechám seznam definic sloupců. (Lze vylepšit, viz níže.)
Řešení
Udělal jsem několik změn ve vašem přístupu, aby zazářil.
Spouštěcí funkce pro 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;
Spouštěcí funkce pro 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;
Spouštěče:
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 demonstrující INSERT
a UPDATE
.
Hlavní body
-
Zahrňte název schématu, aby byl odkaz na tabulku jednoznačný. Ve stejné databázi může být více instancí stejného názvu tabulky ve více schématech!
-
Dotaz
pg_attribute
namístoinformation_schema.columns
. To je méně přenosné, ale hodně rychlejší a umožňuje mi používat tabulku-OID.- Jak zkontrolovat, zda tabulka v daném schématu existuje
-
Názvy tabulek NEJSOU bezpečné proti SQLi při zpracování jako řetězců jako při vytváření dotazů pro dynamické SQL. Escape pomocí
quote_ident()
neboformat()
nebo s typem identifikátoru objektu. To zahrnuje speciální proměnné spouštěcí funkceTG_TABLE_SCHEMA
aTG_TABLE_NAME
! -
Přetypovat na typ identifikátoru objektu
regclass
pro potvrzení platnosti názvu tabulky a získání OID pro vyhledávání v katalogu. -
Volitelně použijte
format()
pro bezpečné sestavení dynamického řetězce dotazu. -
Není potřeba dynamický SQL pro první dotaz na katalogové tabulky. Rychlejší, jednodušší.
-
Použijte
RETURN NEW
místoRETURN NULL
v těchto spouštěcích funkcích, pokud nevíte, co děláte. (NULL
by zrušiloINSERT
pro aktuální řádek.) -
Tato jednoduchá verze předpokládá, že každá tabulka (a zobrazení) má jedinečný sloupec s názvem
id
. Sofistikovanější verze může používat primární klíč dynamicky. -
Funkce pro
UPDATE
umožňuje, aby sloupce zobrazení a tabulky byly v libovolném pořadí , pokud je sada stejná. Funkce proINSERT
očekává, že sloupce zobrazení a tabulky budou v stejném pořadí . Pokud chcete povolit libovolné pořadí, přidejte seznam definic sloupců doINSERT
příkaz, stejně jako uUPDATE
. -
Aktualizovaná verze také zahrnuje změny v
id
pomocíOLD
navíc.