sql >> Base de Datos >  >> RDS >> PostgreSQL

Divida el registro devuelto por la función en varias columnas

En Postgres 9.3 o posterior, esto se resuelve mejor con un LATERAL unirse:

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 la evaluación repetida de la función (para cada columna en la salida; la función debe llamarse para cada fila de entrada de cualquier manera).
LEFT JOIN LATERAL ... ON true para evitar eliminar filas del lado izquierdo si la función no devuelve ninguna fila:

  • ¿Cuál es la diferencia entre LATERAL y una subconsulta en PostgreSQL?

Seguimiento en su comentario:

solo las columnas expandidas producidas por la llamada de función

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;

Pero como no le importan otras columnas, puede simplificar a:

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 es un CROSS JOIN LATERAL implícito . Si la función puede devolver "ninguna fila" ocasionalmente, el resultado puede ser diferente:no obtenemos valores NULL para las filas, esas filas simplemente se eliminan y LIMIT ya no los cuenta.

En versiones anteriores (o en general) también puede simplemente descomponer el tipo compuesto con la sintaxis correcta:

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;

El inconveniente es que la función se evalúa una vez para cada columna en el resultado de la función debido a una debilidad en el planificador de consultas de Postgres. Es mejor mover la llamada a una subconsulta o CTE y descomponer el tipo de fila en el SELECT externo. . Me gusta:

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;

Pero debe nombrar columnas individuales y no puede salirse con la suya con SELECT * a menos que esté de acuerdo con el tipo de fila en el resultado de forma redundante. Relacionado:

  • Evite varias llamadas a la misma función al expandir el resultado compuesto
  • ¿Cómo evitar múltiples evaluaciones de funciones con la sintaxis (func()).* en una consulta SQL?