PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zaktualizuj wiele kolumn w funkcji wyzwalacza w plpgsql

Chociaż odpowiedź @Gary'ego jest technicznie poprawna, nie wspomina on, że PostgreSQL tak wesprzyj ten formularz:

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

Przeczytaj instrukcję UPDATE jeszcze raz.

Wciąż trudno jest poradzić sobie z dynamicznym SQL. Ponieważ nie określiłeś, zakładam prosty przypadek, w którym widoki składają się z tych samych kolumn, co ich tabele bazowe.

CREATE VIEW tbl_view AS SELECT * FROM tbl;

Problemy

  • Specjalny rekord NEW nie jest widoczny w EXECUTE . Zdaję NEW jako pojedynczy parametr z USING klauzula EXECUTE .

  • Jak wspomniano, UPDATE z list-form wymaga indywidualnych wartości . Używam podwyboru, aby podzielić rekord na poszczególne kolumny:

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

    (Nawiasy wokół $1 nie są opcjonalne.) To pozwala mi po prostu użyć dwóch list kolumn zbudowanych za pomocą string_agg() z tabeli katalogowej:jedna z kwalifikacją stołu i jedna bez kwalifikacji.

  • Nie można przypisać wartości wiersza jako całości do poszczególnych kolumn. Instrukcja:

    Zgodnie ze standardem wartością źródłową dla nawiasów podlisty nazw kolumn docelowych może być dowolne wyrażenie o wartości wiersza, dające poprawną liczbę kolumn. PostgreSQL pozwala tylko, aby wartośćźródłowa była konstruktorem wiersza lub sub-SELECT .

  • INSERT jest zaimplementowany prostszy. Zakładając, że struktura widoku i tabeli są identyczne, pomijam listę definicji kolumn. (Można poprawić, patrz poniżej.)

Rozwiązanie

Wprowadziłem kilka zmian w Twoim podejściu, aby zabłysło.

Funkcja wyzwalania dla 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;

Funkcja wyzwalania dla 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;

Wyzwalacze:

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();

Skrzypce SQL demonstrowanie INSERT i UPDATE .

Główne punkty

  • Dołącz nazwę schematu, aby odwołanie do tabeli było jednoznaczne. Może istnieć wiele wystąpień tej samej nazwy tabeli w tej samej bazie danych w wielu schematach!

  • Zapytanie pg_attribute zamiast information_schema.columns . To mniej przenośne, ale dużo szybciej i pozwala mi używać table-OID.

    • Jak sprawdzić, czy tabela istnieje w danym schemacie
  • Nazwy tabel NIE są bezpieczne dla SQLi gdy są obsługiwane jako ciągi, jak przy tworzeniu zapytań dla dynamicznego SQL. Ucieknij za pomocą quote_ident() lub format() lub z typem identyfikatora obiektu. Obejmuje to specjalne zmienne funkcji wyzwalacza TG_TABLE_SCHEMA i TG_TABLE_NAME !

  • Przesyłaj do typu identyfikatora obiektu regclass aby potwierdzić, że nazwa tabeli jest prawidłowa i uzyskać OID dla wyszukiwania katalogu.

  • Opcjonalnie użyj format() aby bezpiecznie zbudować dynamiczny ciąg zapytania.

  • Nie ma potrzeby dynamicznego SQL dla pierwszego zapytania w tabelach katalogu. Szybciej, prościej.

  • Użyj RETURN NEW zamiast RETURN NULL w tych funkcjach wyzwalania, chyba że wiesz, co robisz. (NULL anuluje INSERT dla bieżącego wiersza).

  • Ta prosta wersja zakłada, że ​​każda tabela (i widok) ma unikalną kolumnę o nazwie id . Bardziej wyrafinowana wersja może dynamicznie używać klucza podstawowego.

  • Funkcja UPDATE umożliwia ustawienie kolumn widoku i tabeli w dowolnej kolejności , o ile zestaw jest taki sam. Funkcja INSERT oczekuje, że kolumny widoku i tabeli będą w identycznej kolejności . Jeśli chcesz zezwolić na dowolną kolejność, dodaj listę definicji kolumn do INSERT polecenie, tak jak z UPDATE .

  • Zaktualizowana wersja obejmuje również zmiany w id kolumna za pomocą OLD dodatkowo.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kontrola wersji PostgreSQL z Atlassian Bitbucket

  2. Jak pracować z PGpoint dla Geolokalizacji przy użyciu PostgreSQL?

  3. Jaki jest najszybszy sposób na zrobienie zbiorczego wstawiania do Postgresa?

  4. Jak wykonać kopię zapasową bazy danych postgresql z poziomu psql?

  5. Jaka jest równoważna składnia PostgreSQL dla Oracle CONNECT BY ... START WITH?