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.