PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Актуализирайте множество колони в тригерна функция в plpgsql

Въпреки че отговорът на @Gary е технически правилен, той пропуска да спомене, че PostgreSQL прави поддържа този формуляр:

UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)

Прочетете ръководството на UPDATE още веднъж.

Все още е трудно да го свърши с динамичен SQL. Тъй като не сте посочили, предполагам прост случай, при който изгледите се състоят от същите колони като техните основни таблици.

CREATE VIEW tbl_view AS SELECT * FROM tbl;

Проблеми

  • Специалният запис NEW не се вижда вътре в EXECUTE . Поддържам NEW като единичен параметър с USING клауза на EXECUTE .

  • Както беше обсъдено, UPDATE със списъчна форма се нуждае от отделни стойности . Използвам подселекция, за да разделя записа на отделни колони:

    UPDATE ...
    FROM  (SELECT ($1).*) x
    

    (Скоба около $1 не са по избор.) Това ми позволява просто да използвам два списъка с колони, изградени с string_agg() от каталожната таблица:един с и един без квалификация на таблицата.

  • Не е възможно да се присвои стойност на ред като цяло на отделни колони. Ръководството:

    Съгласно стандарта, изходната стойност за подсписък в скоби с имена на целеви колони може да бъде всеки израз със стойност на ред, който дава правилния брой колони. PostgreSQL позволява само стойността на източника да бъде конструктор на ред или подSELECT .

  • INSERT се реализира по-просто. Ако приемем, че структурата на изгледа и таблицата са идентични, пропускам списъка с дефиниции на колони. (Може да се подобри, вижте по-долу.)

Решение

Направих редица актуализации на подхода ви, за да го накарам да блести.

Функция за задействане за 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;

Функция за задействане за 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;

Тригери:

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 демонстриращ INSERT и UPDATE .

Основни точки

  • Включете името на схемата, за да направите препратката към таблицата недвусмислена. Може да има множество екземпляри на едно и също име на таблица в една и съща база данни в множество схеми!

  • Заявка pg_attribute вместо information_schema.columns . Това е по-малко преносимо, но много по-бързо и ми позволява да използвам таблицата-OID.

    • Как да проверите дали таблица съществува в дадена схема
  • Имената на таблици НЕ са безопасни срещу SQLi когато се обработват като низове, както при изграждане на заявки за динамичен SQL. Избягайте с quote_ident() или format() или с тип идентификатор на обект. Това включва специалните променливи на функцията за задействане TG_TABLE_SCHEMA и TG_TABLE_NAME !

  • Прехвърляне към идентификатора на обект тип regclass за да потвърдите, че името на таблицата е валидно и да получите OID за търсене в каталога.

  • По избор използвайте format() за безопасно изграждане на динамичния низ на заявка.

  • Няма нужда от динамичен SQL за първата заявка в каталожните таблици. По-бързо, по-лесно.

  • Използвайте RETURN NEW вместо RETURN NULL в тези тригерни функции, освен ако не знаете какво правите. (NULL ще отмени INSERT за текущия ред.)

  • Тази проста версия предполага, че всяка таблица (и изглед) има уникална колона с име id . По-сложна версия може да използва първичния ключ динамично.

  • Функцията за UPDATE позволява колоните на изглед и таблица да бъдат в произволен ред , стига наборът да е същият. Функцията за INSERT очаква колоните изглед и таблица да бъдат в идентичен ред . Ако искате да разрешите произволен ред, добавете списък с дефиниции на колони към INSERT команда, точно както с UPDATE .

  • Актуализираната версия обхваща и промените в id колона с помощта на OLD допълнително.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да декларирам локални променливи в postgresql?

  2. Изчисляване на проценти със заявка GROUP BY

  3. КАСКАДНО ИЗТРИВАНЕ само веднъж

  4. Windows PSQL команден ред:има ли начин да се разреши влизане без парола?

  5. ST_DWithin приема параметър като градус, а не метри, защо?