Dada la tabla:
Column | Type
id | integer
latitude | numeric(9,6)
longitude | numeric(9,6)
speed | integer
equipment_id | integer
created_at | timestamp without time zone
Indexes:
"geoposition_records_pkey" PRIMARY KEY, btree (id)
La tabla tiene 20 millones de registros que no son, relativamente hablando, un gran número. Pero hace que los escaneos secuenciales sean lentos.
¿Cómo puedo obtener el último registro ( max(created_at)
) de cada uno equipment_id
?
He intentado las dos consultas siguientes, con varias variantes que he leído a través de muchas respuestas de este tema:
select max(created_at),equipment_id from geoposition_records group by equipment_id;
select distinct on (equipment_id) equipment_id,created_at
from geoposition_records order by equipment_id, created_at desc;
También he intentado crear índices btree para, equipment_id,created_at
pero Postgres descubre que usar un seqscan es más rápido. Forzar enable_seqscan = off
tampoco sirve de nada, ya que leer el índice es tan lento como la exploración secuencial, probablemente peor.
La consulta debe ejecutarse periódicamente, devolviendo siempre la última.
Usando Postgres 9.3.
Explicar / analizar (con 1.7 millones de registros):
set enable_seqscan=true;
explain analyze select max(created_at),equipment_id from geoposition_records group by equipment_id;
"HashAggregate (cost=47803.77..47804.34 rows=57 width=12) (actual time=1935.536..1935.556 rows=58 loops=1)"
" -> Seq Scan on geoposition_records (cost=0.00..39544.51 rows=1651851 width=12) (actual time=0.029..494.296 rows=1651851 loops=1)"
"Total runtime: 1935.632 ms"
set enable_seqscan=false;
explain analyze select max(created_at),equipment_id from geoposition_records group by equipment_id;
"GroupAggregate (cost=0.00..2995933.57 rows=57 width=12) (actual time=222.034..11305.073 rows=58 loops=1)"
" -> Index Scan using geoposition_records_equipment_id_created_at_idx on geoposition_records (cost=0.00..2987673.75 rows=1651851 width=12) (actual time=0.062..10248.703 rows=1651851 loops=1)"
"Total runtime: 11305.161 ms"
NULL
valores enequipment_id
el porcentaje esperado está por debajo del 0.1%Respuestas:
Después de todo, un índice b-tree simple de varias columnas debería funcionar:
¿Por qué
DESC NULLS LAST
?Función
Si no puede hablar con sentido en el planificador de consultas, una función que recorra la tabla del equipo debería ser la solución. Buscar un equipo_id a la vez utiliza el índice. Para un número pequeño (57 a juzgar por su
EXPLAIN ANALYZE
salida), eso es rápido.¿Es seguro asumir que tienes una
equipment
mesa?También es una buena llamada:
Subconsultas correlacionadas
Ahora que lo pienso, usando esta
equipment
tabla, podría hacer el trabajo sucio con subconsultas poco correlacionadas para un gran efecto:El rendimiento es muy bueno.
LATERAL
únete a Postgres 9.3+Explicación detallada:
Rendimiento similar al de la subconsulta correlacionada. Comparación del rendimiento de
max()
,DISTINCT ON
función, subconsulta correlacionada yLATERAL
en esto:SQL Fiddle .
fuente
Intento 1
Si
equipment
mesa ygeoposition_records(equipment_id, created_at desc)
entonces lo siguiente funciona para mí:
No pude obligar a PG a hacer una consulta rápida para determinar tanto la lista de
equipment_id
sy la relacionadamax(created_at)
. ¡Pero voy a intentarlo de nuevo mañana!Intento 2
Encontré este enlace: http://zogovic.com/post/44856908222/optimizing-postgresql-query-for-distinct-values Combinando esta técnica con mi consulta del intento 1, obtengo:
y esto funciona RAPIDO! Pero tu necesitas
geoposition_records(equipment_id, created_at desc)
.fuente