DISTINCT ON
suele ser más simple y rápido para esto en PostgreSQL .
(Para obtener información sobre la optimización del rendimiento para determinadas cargas de trabajo, consulte a continuación).
SELECT DISTINCT ON (customer)
id, customer, total
FROM purchases
ORDER BY customer, total DESC, id;
O más corto (si no es tan claro) con números ordinales de columnas de salida:
SELECT DISTINCT ON (2)
id, customer, total
FROM purchases
ORDER BY 2, 3 DESC, 1;
Si total
puede ser NULL (no le hará daño de ninguna manera, pero querrá hacer coincidir los índices existentes):
...
ORDER BY customer, total DESC NULLS LAST, id;
Puntos principales
DISTINCT ON
es una extensión PostgreSQL del estándar (donde solo DISTINCT
en general SELECT
la lista está definida).
Enumere cualquier cantidad de expresiones en DISTINCT ON
cláusula, el valor de fila combinado define duplicados. El manual:
Obviamente, dos filas se consideran distintas si difieren en al menos un valor de columna. Los valores nulos se consideran iguales en esta comparación.
Énfasis en negrita mío.
DISTINCT ON
se puede combinar con ORDER BY
. Expresiones principales en ORDER BY
debe estar en el conjunto de expresiones en DISTINCT ON
, pero puede reorganizar el orden entre ellos libremente. Ejemplo.
Puede agregar adicionales expresiones a ORDER BY
para elegir una fila en particular de cada grupo de pares. O, como dice el manual:
El DISTINCT ON
la(s) expresión(es) debe(n) coincidir con el ORDER BY
más a la izquierda expresión(es). El ORDER BY
La cláusula normalmente contendrá expresiones adicionales que determinan la precedencia deseada de las filas dentro de cada DISTINCT ON
grupo.
Agregué id
como último elemento para desempatar:
"Seleccione la fila con el id
más pequeño de cada grupo que comparte el total
más alto ."
Para ordenar los resultados de una manera que no esté de acuerdo con el orden de clasificación que determina el primero por grupo, puede anidar la consulta anterior en una consulta externa con otro ORDER BY
. Ejemplo.
Si total
puede ser NULL, muy probablemente quiere la fila con el mayor valor no nulo. Agregar NULLS LAST
como demostrado. Ver:
- ¿Ordenar por columna ASC, pero los valores NULL primero?
El SELECT
lista no está limitado por expresiones en DISTINCT ON
o ORDER BY
de cualquier manera. (No es necesario en el caso simple anterior):
-
Usted no tiene que hacerlo incluir cualquiera de las expresiones en
DISTINCT ON
oORDER BY
. -
Tu puedes incluir cualquier otra expresión en el
SELECT
lista. Esto es fundamental para reemplazar consultas mucho más complejas con subconsultas y funciones de agregación/ventana.
Probé con las versiones 8.3 a 13 de Postgres. Pero la característica ha estado allí al menos desde la versión 7.1, así que básicamente siempre.
Índice
El perfecto El índice de la consulta anterior sería un índice de varias columnas que abarcaría las tres columnas en secuencia coincidente y con orden de clasificación coincidente:
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Puede ser demasiado especializado. Pero utilícelo si el rendimiento de lectura para la consulta en particular es crucial. Si tiene DESC NULLS LAST
en la consulta, use lo mismo en el índice para que el orden de clasificación coincida y el índice sea aplicable.
Eficacia / Optimización del rendimiento
Sopese el costo y el beneficio antes de crear índices personalizados para cada consulta. El potencial del índice anterior depende en gran medida de la distribución de datos .
El índice se usa porque entrega datos preordenados. En Postgres 9.2 o posterior, la consulta también se puede beneficiar de un análisis de solo índice si el índice es más pequeño que la tabla subyacente. Sin embargo, el índice debe escanearse en su totalidad.
Para pocos filas por cliente (alta cardinalidad en la columna customer
), esto es muy eficiente. Más aún si necesita una salida ordenada de todos modos. El beneficio se reduce con un número creciente de filas por cliente.
Idealmente, tiene suficiente work_mem
para procesar el paso de clasificación involucrado en la RAM y no derramarlo en el disco. Pero generalmente establecer work_mem
también alta puede tener efectos adversos. Considere SET LOCAL
para consultas excepcionalmente grandes. Encuentra cuánto necesitas con EXPLAIN ANALYZE
. Mención de "Disco: " en el paso de clasificación indica la necesidad de más:
- Parámetro de configuración work_mem en PostgreSQL en Linux
- Optimizar consulta simple usando ORDER BY fecha y texto
Para muchos filas por cliente (baja cardinalidad en la columna customer
), un escaneo de índice suelto (también conocido como "escaneo de omisión") sería (mucho) más eficiente, pero eso no está implementado hasta Postgres 14. (Se está desarrollando una implementación para escaneos de solo índice para Postgres 15. Consulte aquí y aquí).
Para ahora, existen técnicas de consulta más rápidas para sustituir esto. En particular, si tiene una mesa separada con clientes únicos, que es el caso de uso típico. Pero también si no:
- SELECT DISTINCT es más lento de lo esperado en mi tabla en PostgreSQL
- Optimizar la consulta GROUP BY para recuperar la fila más reciente por usuario
- Optimizar consulta máxima grupal
- Consulta las últimas N filas relacionadas por fila
Puntos de referencia
Ver respuesta separada.