J'utilise postgres 9.4.
Le messages
a le schéma suivant: les messages appartiennent à feed_id, et a posted_at, les messages peuvent également avoir un message parent (en cas de réponses).
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)
Je souhaite renvoyer tous les messages commandés par share_count
, mais pour chacun parent_id
, je souhaite renvoyer un seul message. c'est-à-dire, si plusieurs messages ont le même parent_id
, alors seul le dernier ( posted_at
) est retourné. Le parent_id
peut être nul, les messages avec null parent_id
doivent tous retourner.
La requête que j'ai utilisée est:
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;
Voici le http://sqlfiddle.com/#!15/588e5/1/0 , dans le SQL Fiddle, j'ai défini le schéma, la requête exacte et le résultat attendu.
Mais les performances de la requête sont lentes une fois que la table des messages devient volumineuse. J'ai essayé d'ajouter plusieurs index de tri, mais il ne semble pas utiliser l'index. Voici l'explication: http://explain.depesz.com/s/Sv2
Comment puis-je créer un index correct?
la source
ORDER BY
dans la sous-requête est totalement inutile. De plus, le plan lié ne peut pas être le résultat de la requête publiée - il n'est pas fait mentionmetadata
, par exemple.feed_id
etposted_at
et vous ne l'avez pas mentionnémetadata
du tout, qui semble être un type JSON? Veuillez réparer votre question pour la rendre cohérente. Vous sélectionnez> 500k lignes dans le CTE ... Combien de lignes sont dans le tableau? Quel pourcentage de lignes sélectionnez-vous généralement dans le CTE? Quel est le pourcentage de lignesparent_id IS NULL
? Considérez les informations dans la balise [postgresql-performance] pour les questions de performances.parent_id
? (min / moy / max)metadata
. Actuellement, le tableau des messages contient des données de 10 mil, mais augmente rapidement. Je pense me séparer en tables de partition pour chaque feed_id. Étant donné que je ne récupère que par identifiant de flux. le pourcentage de parent_id nul vs non nul est d'environ 60% / 40%. une extraction typique représente environ 1 à 2% du tableau. (environ 100K messages) Les performances pour 100K sont d'environ 1s, mais une fois à 500K +, il utilise un index bitmap et prend normalement 10s.Réponses:
Requete
Cette requête devrait être sensiblement plus rapide dans tous les cas:
Le CTE ne fait rien ici qu'une sous-requête simple ne pourrait pas fournir également. Et un CTE introduit une barrière d'optimisation car il est exécuté séparément et son résultat est matérialisé.
Vous disposez d'un niveau de sous-requête de plus que ce dont vous avez réellement besoin.
L'expression
(COALESCE(parent_id, message_id)
n'est pas compatible avec un index simple, vous auriez besoin d'un index sur cette expression. Mais cela peut ne pas être très utile non plus, selon la distribution des données. Suivez mes liens ci-dessous pour des informations détaillées.Le fait de diviser le cas simple
parent_id IS NULL
en un autreSELECT
peut ou non fournir l'optimum. Surtout pas, si c'est un cas rare de toute façon, auquel cas une requête combinée avec un index sur(COALESCE(parent_id, message_id)
peut mieux fonctionner. D'autres considérations s'appliquent ...Indices
Surtout lorsqu'il est pris en charge avec ces indices:
Les deux indices partiels couvrent l'ensemble du tableau ensemble et ont à peu près la même taille qu'un seul indice total.
Les deux dernières colonnes
parent_id, message_id
n'ont de sens que si vous en obtenez des analyses d'index uniquement . Sinon, supprimez-les des deux indices.SQL Fiddle.
Selon les détails manquants,
DISTINCT ON
peut être ou non la meilleure technique de requête à cet effet. Lisez l'explication détaillée ici:Et des alternatives peut-être plus rapides ici:
la source