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
NEWnení vidět vEXECUTE. Prošel jsemNEWjako jeden parametr sUSINGklauzuleEXECUTE. -
Jak bylo uvedeno,
UPDATEse 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
$1nejsou 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. -
INSERTje 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_attributenamí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_SCHEMAaTG_TABLE_NAME! -
Přetypovat na typ identifikátoru objektu
regclasspro 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 NEWmístoRETURN NULLv těchto spouštěcích funkcích, pokud nevíte, co děláte. (NULLby zrušiloINSERTpro 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
UPDATEumožňuje, aby sloupce zobrazení a tabulky byly v libovolném pořadí , pokud je sada stejná. Funkce proINSERTočekává, že sloupce zobrazení a tabulky budou v stejném pořadí . Pokud chcete povolit libovolné pořadí, přidejte seznam definic sloupců doINSERTpříkaz, stejně jako uUPDATE. -
Aktualizovaná verze také zahrnuje změny v
idpomocíOLDnavíc.