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

Use algo como TOP con GROUP BY

Puede recuperar cómodamente al pasajero con el nombre más largo por grupo con DISTINCT ON .

Pero no veo forma de combinar eso (o cualquier otra forma simple) con su consulta original en un solo SELECT . Sugiero unir dos subconsultas separadas:

SELECT *
FROM  (  -- your original query
   SELECT orig
        , count(*) AS flight_cnt
        , count(distinct passenger) AS pass_cnt
        , percentile_cont(0.5) WITHIN GROUP (ORDER BY bags) AS bag_cnt_med
   FROM   table1
   GROUP  BY orig
   ) org_query
JOIN  (  -- my addition
   SELECT DISTINCT ON (orig) orig, passenger AS pass_max_len_name
   FROM   table1
   ORDER  BY orig, length(passenger) DESC NULLS LAST
   ) pas USING (orig);

USING en la cláusula de unión convenientemente solo genera una instancia de orig , por lo que simplemente puede usar SELECT * en el exterior SELECT .

Si passenger puede ser NULL, es importante agregar NULLS LAST :

De varios nombres de pasajeros con la misma longitud máxima en el mismo grupo, obtienes una selección arbitraria - a menos que agregue más expresiones a ORDER BY como desempate. Explicación detallada en la respuesta enlazada arriba.

¿Rendimiento?

Por lo general, un solo escaneo es superior, especialmente con escaneos secuenciales.

La consulta anterior utiliza dos escaneos (quizás escaneos de índice / solo índice). Pero el segundo escaneo es relativamente barato a menos que la tabla sea demasiado grande para caber en el caché (en su mayoría). Lukas sugirió una consulta alternativa con solo un único SELECT agregando:

, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1]  -- I'd add NULLS LAST

La idea es inteligente, pero la última vez que probé , array_agg con ORDER BY no se desempeñó tan bien. (La sobrecarga de ORDER BY por grupo es sustancial, y el manejo de matrices también es costoso).

El mismo enfoque puede ser más económico con una función agregada personalizada first() como se indica en Postgres Wiki aquí . O, aún más rápido, con una versión escrita en C, disponible en PGXN . Elimina el costo adicional para el manejo de matrices, pero aún necesitamos ORDER BY por grupo . Puede ser más rápido solo para unos pocos grupos. Luego agregaría:

 , first(passenger ORDER BY length(passenger) DESC NULLS LAST)

Gordon y Lukas también mencione la función de ventana first_value() . Las funciones de ventana se aplican después Funciones agregadas. Para usarlo en el mismo SELECT , necesitaríamos agregar passenger de alguna manera first - catch 22. Gordon resuelve esto con una subconsulta - otro candidato para un buen desempeño con Postgres estándar.

first() hace lo mismo sin subconsulta y debería ser más simple y un poco más rápido. Pero aún así no será más rápido que un DISTINCT ON separado para la mayoría de los casos con pocas filas por grupo. Para muchas filas por grupo, una técnica CTE recursiva suele ser más rápida. Hay técnicas aún más rápidas si tiene una tabla separada que contiene todos los orig únicos y relevantes valores. Detalles:

La mejor solución depende de varios factores. La prueba del budín está en comerlo. Para optimizar el rendimiento, debe probar con su configuración. La consulta anterior debería estar entre las más rápidas.