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 wEXECUTE
. ZdajęNEW
jako pojedynczy parametr zUSING
klauzulaEXECUTE
. -
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
zamiastinformation_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()
lubformat()
lub z typem identyfikatora obiektu. Obejmuje to specjalne zmienne funkcji wyzwalaczaTG_TABLE_SCHEMA
iTG_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
zamiastRETURN NULL
w tych funkcjach wyzwalania, chyba że wiesz, co robisz. (NULL
anulujeINSERT
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. FunkcjaINSERT
oczekuje, że kolumny widoku i tabeli będą w identycznej kolejności . Jeśli chcesz zezwolić na dowolną kolejność, dodaj listę definicji kolumn doINSERT
polecenie, tak jak zUPDATE
. -
Zaktualizowana wersja obejmuje również zmiany w
id
kolumna za pomocąOLD
dodatkowo.