sql >> Databáze >  >> RDS >> PostgreSQL

Aktualizujte více sloupců ve spouštěcí funkci v plpgsql

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 v EXECUTE . Prošel jsem NEW jako jeden parametr s USING klauzule EXECUTE .

  • 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ísto information_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() nebo format() nebo s typem identifikátoru objektu. To zahrnuje speciální proměnné spouštěcí funkce TG_TABLE_SCHEMA a TG_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ísto RETURN NULL v těchto spouštěcích funkcích, pokud nevíte, co děláte. (NULL by zrušilo INSERT 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 pro INSERT 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ů do INSERT příkaz, stejně jako u UPDATE .

  • Aktualizovaná verze také zahrnuje změny v id pomocí OLD navíc.



  1. Postgres je nejlepší databáze – Důvod č. 2:Licence

  2. Jak nainstalovat PostgreSQL na macOS

  3. Jak mohu odstranit duplicitní řádky?

  4. PLS-00428:V tomto příkazu SELECT se očekává klauzule INTO