Lors de la récupération de toutes ou de la plupart des lignes d'une table, le moyen le plus rapide pour ce type de requête consiste généralement à agréger/désambiguïser d'abord et rejoignez plus tard :
SELECT *
FROM products p
JOIN (
SELECT DISTINCT ON (product_id) *
FROM meta
ORDER BY product_id, id DESC
) m ON m.product_id = p.id;
Plus il y a de lignes dans meta
par ligne dans products
, plus l'impact sur les performances est important.
Bien sûr, vous voudrez ajouter un ORDER BY
clause dans la sous-requête définit quel ligne à sélectionner dans chaque ensemble de la sous-requête. @Craig et @Clodoaldo vous en ont déjà parlé. Je retourne le meta
ligne avec le id
le plus élevé .
Violon SQL.
Détails pour DISTINCT ON
:
- Sélectionner la première ligne de chaque groupe GROUP BY ?
Optimiser les performances
Pourtant, ce n'est pas toujours la solution la plus rapide. Selon la distribution des données, il existe divers autres styles de requête. Pour ce cas simple impliquant une autre jointure, celle-ci s'est exécutée considérablement plus rapidement dans un test avec de grandes tables :
SELECT p.*, sub.meta_id, m.product_id, m.price, m.flag
FROM (
SELECT product_id, max(id) AS meta_id
FROM meta
GROUP BY 1
) sub
JOIN meta m ON m.id = sub.meta_id
JOIN products p ON p.id = sub.product_id;
Si vous n'utilisez pas l'id
non descriptif en tant que noms de colonnes, nous ne rencontrerions pas de collisions de noms et pourrions simplement écrire SELECT p.*, m.*
. (Je jamais utiliser id
comme nom de colonne.)
Si la performance est votre exigence primordiale, envisagez d'autres options :
- une
MATERIALIZED VIEW
avec des données pré-agrégées demeta
, si vos données ne changent pas (beaucoup). - un CTE récursif émulant un balayage d'index lâche pour un gros
meta
table avec beaucoup lignes par produit (relativement peu deproduct_id
distincts ).
C'est la seule façon que je connaisse d'utiliser un index pour une requête DISTINCT sur toute la table.