Estou desenvolvendo uma função definida pelo usuário que recebe dois argumentos:
create or replace function gesio(
events_table_in regclass,
events_table_out regclass)
returns void as $$ ... $$
events_table_in
e events_table_out
têm exatamente o mesmo esquema.
Explicado de forma simples, eu percorro os registros de events_table_in
, manipulo os registros e quero anexar (inserir) os registros manipulados events_table_out
da seguinte maneira:
OPEN recCurs FOR execute
format('SELECT * FROM %s order by session_id, event_time', event_table_in);
LOOP
FETCH recCurs into rec;
if not found then
exit;
end if;
-- 1. do something with rec
-- 2. insert the rec into events_table_out
end loop;
Como posso salvar o rec
em events_table_out
?
Existe uma solução apenas com PL/pgSQL. Simples e elegante também. Coisas bem avançadas, no entanto.
Requer Postgres 9.0 ou posterior (possível solução alternativa para versões mais antigas).
Ligue (importante!):
t
et1
sendo tabelas com esquema idêntico.O parâmetro polimórfico (
anyelement
) só é necessário se você precisar de seu valor ou tipo de dados para o cálculo no corpo da função. Caso contrário, você pode simplificar como demonstrado nesta resposta posterior:Ingredientes principais
Cursor implícito de um
FOR
loop em vez de cursor explícito. Isso é geralmente preferível.Tipos polimórficos
Tipos de identificador de objeto
SQL dinâmico em plpgsql
VALUES
pode receber um tipo de linha diretamente.Um obstáculo a ser superado é que variáveis dentro da função não podem ser definidas como do tipo polimórfico
anyelement
(ainda). Esta resposta relacionada no SO explica a solução. Fornece uma solução alternativa para versões mais antigas também.Estou entregando um
NULL
valor de typet
, que serve a três propósitos:O valor do primeiro parâmetro é descartado. Basta usar
NULL
.Considere esta resposta relacionada no SO com mais detalhes . A parte mais interessante é o último capítulo "Vários tipos de tabelas completas" .
db<>fiddle aqui
Velho sqlfiddle
Se seus cálculos não forem muito sofisticados, você poderá substituir o loop por uma única instrução SQL dinâmica, que normalmente é mais rápida.
Infelizmente não é fácil analisar o
RECORD
tipo usando PL/pgSQL. Se a estrutura das tabelas passadas nos argumentos for sempre a mesma de alguma outra tabela ou tipo, você poderá usar esse tipo diretamente em vez deRECORD
e, em seguida, usar o seguinte:Mas isso não funcionará com o
RECORD
tipo. A única solução que consigo pensar é criar a consulta manualmente. Mas o PL/pgSQL não permite obter dinamicamente as chaves de umRECORD
tipo. Então você tem que usar algumas ferramentas externas. O melhor (na minha opinião) para esse tipo de trabalho é ahstore
extensão . Uma vez instalado, você pode criá-lo em seu banco de dados (o seguinte funciona apenas na versão 9.1+, antes você deve fazê-lo manualmente):Agora. Você pode converter um
RECORD
tipo em umhstore
tipo, usandohstore(recCurs)
, e assim você pode iterar dinamicamente sobre suas chaves e valores com aeach
função:Claro, só funcionará se a tabela "apontada" por
events_table_out
tiver todas as colunas queevents_table_in
possui (a primeira pode ter mais colunas).RESUMINDO: sempre que você quiser algum tipo de dado chave/valor dinâmico em PL/pgSQL, e
RECORD
não é suficiente,hstore
pode ser usado.Eu precisava fazer algo parecido e acabei usando o INSERT do SELECT. Preciso duplicar registros na mesma tabela mas alterando um campo. Talvez não seja a solução mais elegante, mas funciona.
Então, eu chamo a função assim:
Isso duplica os registros da pessoa com id = 1 para a pessoa com id = 2.