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

Utilisez quelque chose comme TOP avec GROUP BY

Vous pouvez facilement récupérer le passager avec le nom le plus long par groupe avec DISTINCT ON .

Mais je ne vois aucun moyen de combiner cela (ou tout autre moyen simple) avec votre requête d'origine dans un seul SELECT . Je suggère de joindre deux sous-requêtes distinctes :

SELECT *
FROM  (  -- your original query
   SELECT orig
        , count(*) AS flight_cnt
        , count(distinct passenger) AS pass_cnt
        , percentile_cont(0.5) WITHIN GROUP (ORDER BY bags) AS bag_cnt_med
   FROM   table1
   GROUP  BY orig
   ) org_query
JOIN  (  -- my addition
   SELECT DISTINCT ON (orig) orig, passenger AS pass_max_len_name
   FROM   table1
   ORDER  BY orig, length(passenger) DESC NULLS LAST
   ) pas USING (orig);

USING dans la clause de jointure ne produit qu'une seule instance de orig , vous pouvez donc simplement utiliser SELECT * dans le SELECT extérieur .

Si passenger peut être NULL, il est important d'ajouter NULLS LAST :

À partir de plusieurs noms de passagers avec la même longueur maximale dans le même groupe, vous obtenez un choix arbitraire - à moins que vous n'ajoutiez d'autres expressions à ORDER BY comme bris d'égalité. Explication détaillée dans la réponse liée ci-dessus.

Performances ?

En règle générale, une seule analyse est supérieure, en particulier avec des analyses séquentielles.

La requête ci-dessus utilise deux scans (peut-être des scans d'index / d'index uniquement). Mais le deuxième scan est relativement bon marché à moins que la table ne soit trop grande pour tenir dans le cache (principalement). Lukas a suggéré une requête alternative avec seulement un single SELECT ajoutant :

, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1]  -- I'd add NULLS LAST

L'idée est intelligente, mais la dernière fois que j'ai testé , array_agg avec ORDER BY n'a pas si bien fonctionné. (La surcharge de ORDER BY par groupe est considérable et la gestion des tableaux est également coûteuse.)

La même approche peut être moins chère avec une fonction d'agrégation personnalisée first() comme indiqué dans le Wiki Postgres ici . Ou, plus rapide, encore, avec une version écrite en C, disponible sur PGXN . Élimine le coût supplémentaire pour la gestion des tableaux, mais nous avons toujours besoin de ORDER BY par groupe . Peut être plus rapide pour quelques groupes seulement. Vous ajouteriez alors :

 , first(passenger ORDER BY length(passenger) DESC NULLS LAST)

Gordon et Lukas mentionnez également la fonction de fenêtre first_value() . Les fonctions de fenêtre sont appliquées après fonctions d'agrégation. Pour l'utiliser dans le même SELECT , nous aurions besoin d'agréger passenger en quelque sorte premier - catch 22. Gordon résout ce problème avec une sous-requête - un autre candidat pour de bonnes performances avec Postgres standard.

first() fait la même chose sans sous-requête et devrait être plus simple et un peu plus rapide. Mais ce ne sera toujours pas plus rapide qu'un DISTINCT ON séparé dans la plupart des cas avec quelques rangées par groupe. Pour de nombreuses lignes par groupe, une technique CTE récursive est généralement plus rapide. Il existe encore des techniques plus rapides si vous avez une table séparée contenant tous les orig pertinents et uniques valeurs. Détails :

La meilleure solution dépend de divers facteurs. La preuve du pudding est dans l'alimentation. Pour optimiser les performances, vous devez tester avec votre configuration. La requête ci-dessus devrait être parmi les plus rapides.