PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Dividir o registro retornado pela função em várias colunas


No Postgres 9.3 ou mais tarde, isso é melhor resolvido com um LATERAL Junte-se:
SELECT *
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
LEFT   JOIN LATERAL hi_lo(a.actor_id, length(a.name), ma.movie_id) x ON true
LIMIT  10;

Evita a avaliação repetida da função (para cada coluna na saída - a função precisa ser chamada para cada linha de entrada de qualquer maneira).
LEFT JOIN LATERAL ... ON true para evitar soltar linhas do lado esquerdo se a função não retornar nenhuma linha:
  • Qual ​​é a diferença entre LATERAL e uma subconsulta no PostgreSQL?

Acompanhe seu comentário:

apenas as colunas expandidas produzidas pela chamada de função
SELECT x.*  -- that's all!
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
LEFT   JOIN LATERAL hi_lo(a.actor_id, length(a.name), ma.movie_id) x ON true
LIMIT  10;

Mas como você não se importa com outras colunas, pode simplificar para:
SELECT x.*
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
     , hi_lo(a.actor_id, length(a.name), ma.movie_id) x
LIMIT  10;

Que é um CROSS JOIN LATERAL implícito . Se a função pode realmente retornar "sem linha" ocasionalmente, o resultado pode ser diferente:não obtemos valores NULL para as linhas, essas linhas são apenas eliminadas - e LIMIT não os conta mais.

Em versões mais antigas (ou geralmente) você também pode apenas decompor o tipo composto com a sintaxe correta:
SELECT *, (hi_lo(a.actor_id, length(a.name), ma.movie_id)).*  -- note extra parentheses!
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
LIMIT  10;

A desvantagem é que a função é avaliada uma vez para cada coluna na saída da função devido a uma fraqueza no planejador de consultas do Postgres. É melhor mover a chamada para uma subconsulta ou CTE e decompor o tipo de linha no SELECT externo . Como:
SELECT actor_id, movie_id, (x).*  -- explicit column names for the rest
FROM  (
   SELECT *, hi_lo(a.actor_id, length(a.name), ma.movie_id) AS x
   FROM   actors a 
   JOIN   movies_actors ma on a.actor_id = ma.movie_id 
   LIMIT  10
   ) sub;

Mas você precisa nomear colunas individuais e não pode se safar com SELECT * a menos que você esteja de acordo com o tipo de linha no resultado de forma redundante. Relacionado:
  • Evite várias chamadas na mesma função ao expandir o resultado composto
  • Como evitar várias avaliações de função com a sintaxe (func()).* em uma consulta SQL?