Estoy usando postgres 9.4.
El messages
tiene el siguiente esquema: los mensajes mostrados pertenecen a FEED_ID, y tiene posted_at, también los mensajes pueden tener un mensaje de los padres (en caso de respuestas).
Table "public.messages"
Column | Type | Modifiers
------------------------------+-----------------------------+-----------
message_id | character varying(255) | not null
feed_id | integer |
parent_id | character varying(255) |
posted_at | timestamp without time zone |
share_count | integer |
Indexes:
"messages_pkey" PRIMARY KEY, btree (message_id)
"index_messages_on_feed_id_posted_at" btree (feed_id, posted_at DESC NULLS LAST)
Quiero devolver todos los mensajes ordenados por share_count
, pero para cada uno parent_id
, solo quiero devolver un mensaje. es decir, si varios mensajes tienen lo mismo parent_id
, solo posted_at
se devuelve el último ( ). El parent_id
puede ser nulo, los mensajes con nula parent_id
debe toda la vuelta.
La consulta que utilicé es:
WITH filtered_messages AS (SELECT *
FROM messages
WHERE feed_id IN (7)
AND (posted_at >= '2015-01-01 04:00:00.000000')
AND (posted_at < '2015-04-28 04:00:00.000000'))
SELECT *
FROM (SELECT DISTINCT ON(COALESCE(parent_id, message_id)) parent_id,
message_id,
posted_at,
share_count
FROM filtered_messages
ORDER BY COALESCE(parent_id, message_id), posted_at DESC NULLS LAST
) messages
ORDER BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
Aquí está el http://sqlfiddle.com/#!15/588e5/1/0 , en el violín de SQL, he definido el esquema, la consulta exacta y el resultado esperado.
Pero el rendimiento de la consulta es lento una vez que la tabla de mensajes se hace grande. Intenté agregar múltiples índices de clasificación, pero no parece usar el índice. Aquí está la explicación: http://explain.depesz.com/s/Sv2
¿Cómo puedo crear un índice correcto?
fuente
ORDER BY
en la subconsulta es totalmente inútil. Además, el plan vinculado no puede ser el resultado de la consulta publicadametadata
; por ejemplo, no se menciona nada .feed_id
yposted_at
y no mencionómetadata
en absoluto, ¿cuál parece ser un tipo JSON? Repare su pregunta para que sea coherente. Selecciona> 500k filas en el CTE ... ¿Cuántas filas hay en la tabla? ¿Qué porcentaje de filas normalmente selecciona en el CTE? ¿Qué porcentaje de filas tieneparent_id IS NULL
? Considere la información en la etiqueta [postgresql-performance] para preguntas de rendimiento.parent_id
? (min / avg / max)metadata
. Actualmente, la tabla de mensajes tiene 10 millones de datos, pero aumenta rápidamente. Creo que se separa en tablas de partición para cada feed_id. Como solo estoy buscando por ID de feed. El porcentaje de parent_id nulo vs no nulo es aproximadamente 60% / 40%. una búsqueda típica es alrededor del 1-2% de la tabla. (alrededor de 100K mensajes) El rendimiento para 100K es de alrededor de 1s, pero una vez que llega a 500K + usa el índice de mapa de bits y normalmente toma 10s.Respuestas:
Consulta
Esta consulta debería ser sustancialmente más rápida en cualquier caso:
El CTE no hace nada aquí que una subconsulta simple no pueda entregar también. Y un CTE introduce una barrera de optimización, ya que se ejecuta por separado y su resultado se materializa.
Tiene un nivel de subconsulta más de lo que realmente necesita.
La expresión
(COALESCE(parent_id, message_id)
no es compatible con un índice simple, necesitaría un índice en esa expresión. Pero eso puede no ser muy útil tampoco, dependiendo de la distribución de datos. Siga mis enlaces a continuación para obtener información detallada.Dividir el caso simple de
parent_id IS NULL
en un separadoSELECT
puede o no entregar el óptimo. Especialmente no, si ese es un caso raro de todos modos, en cuyo caso una consulta combinada con un índice(COALESCE(parent_id, message_id)
puede funcionar mejor. Se aplican otras consideraciones ...Índices
Especialmente cuando se admite con estos índices:
Los dos índices parciales cubren toda la tabla juntos y tienen aproximadamente el mismo tamaño que un índice total único.
Las últimas dos columnas
parent_id, message_id
solo tienen sentido si obtiene escaneos de solo índice . De lo contrario, retírelos de ambos índices.SQL Fiddle.
Dependiendo de los detalles que faltan,
DISTINCT ON
puede o no ser la mejor técnica de consulta para este propósito. Lea la explicación detallada aquí:Y posiblemente alternativas más rápidas aquí:
fuente