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 especialNEWnão é visível dentro deEXECUTE. Eu passoNEWcomo um único parâmetro com oUSINGcláusula deEXECUTE.
-
Conforme discutido,UPDATEcom 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$1nã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_attributeem 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_SCHEMAeTG_TABLE_NAME!
-
Transmitir para o tipo de identificador de objetoregclasspara 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 NEWem vez deRETURN NULLnessas funções de gatilho, a menos que você saiba o que está fazendo. (NULLcancelaria oINSERTpara 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 paraUPDATEpermite que as colunas da visualização e da tabela estejam em qualquer ordem , desde que o conjunto seja o mesmo. A função paraINSERTespera 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 aoINSERTcomando, assim como comUPDATE.
-
A versão atualizada também abrange alterações noidcoluna usandoOLDAlém disso.