PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

GROUP ou DISTINCT après JOIN renvoie des doublons

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 de meta , 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 de product_id distincts ).
    C'est la seule façon que je connaisse d'utiliser un index pour une requête DISTINCT sur toute la table.