Embora a resposta de @Gary esteja tecnicamente correta, ele não menciona que o PostgreSQL faz apoie este formulário:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Leia o manual em
UPDATE
mais uma vez. Ainda é complicado fazer isso com SQL dinâmico. Como você não especificou, estou assumindo um caso simples em que as visualizações consistem nas mesmas colunas que suas tabelas subjacentes.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Problemas
-
O registro especialNEW
não é visível dentro deEXECUTE
. Eu passoNEW
como um único parâmetro com oUSING
cláusula deEXECUTE
.
-
Conforme discutido,UPDATE
com forma de lista precisa de valores individuais . Eu uso uma subseleção para dividir o registro em colunas individuais:
UPDATE ... FROM (SELECT ($1).*) x
(Parênteses em torno de$1
não são opcionais.) Isso me permite simplesmente usar duas listas de colunas construídas comstring_agg()
da tabela de catálogo:um com e outro sem qualificação de tabela.
-
Não é possível atribuir um valor de linha como um todo a colunas individuais. O manual:
De acordo com o padrão, o valor de origem para uma sublista entre parênteses de nomes de colunas de destino pode ser qualquer expressão com valor de linha que produza o número correto de colunas. O PostgreSQL só permite que o valor de origem seja um construtor de linha ou um sub-SELECT
.
-
INSERT
é implementado de forma mais simples. Supondo que a estrutura da visão e da tabela sejam idênticas, omito a lista de definição de coluna. (Pode ser melhorado, veja abaixo.)
Solução
Fiz várias atualizações na sua abordagem para fazê-la brilhar.
Função de gatilho para
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;
Função de gatilho para
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;
Gatilhos:
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();
Fiddle SQL demonstrando
INSERT
e UPDATE
. Pontos principais
-
Inclua o nome do esquema para tornar a referência de tabela inequívoca. Pode haver várias instâncias do mesmo nome de tabela no mesmo banco de dados em vários esquemas!
-
Consultapg_attribute
em vez deinformation_schema.columns
. Isso é menos portátil, mas muito mais rápido e me permite usar o table-OID.
- Como verificar se uma tabela existe em um determinado esquema
-
Os nomes das tabelas NÃO são seguros contra SQLi quando tratadas como strings, como na construção de consultas para SQL dinâmico. Escape comquote_ident()
ouformat()
ou com um tipo de identificador de objeto. Isso inclui as variáveis de função de gatilho especialTG_TABLE_SCHEMA
eTG_TABLE_NAME
!
-
Transmitir para o tipo de identificador de objetoregclass
para confirmar que o nome da tabela é válido e obter o OID para a consulta do catálogo.
-
Opcionalmente, useformat()
para construir a string de consulta dinâmica com segurança.
-
Não há necessidade de SQL dinâmico para a primeira consulta nas tabelas do catálogo. Mais rápido, mais simples.
-
UseRETURN NEW
em vez deRETURN NULL
nessas funções de gatilho, a menos que você saiba o que está fazendo. (NULL
cancelaria oINSERT
para a linha atual.)
-
Esta versão simples assume que cada tabela (e visualização) tem uma coluna única chamadaid
. Uma versão mais sofisticada pode usar a chave primária dinamicamente.
-
A função paraUPDATE
permite que as colunas da visualização e da tabela estejam em qualquer ordem , desde que o conjunto seja o mesmo. A função paraINSERT
espera que as colunas da visualização e da tabela estejam em ordem idêntica . Se você deseja permitir uma ordem arbitrária, adicione uma lista de definição de coluna aoINSERT
comando, assim como comUPDATE
.
-
A versão atualizada também abrange alterações noid
coluna usandoOLD
Além disso.