La requête suivante:
SELECT
year, id, rate
FROM h
WHERE year BETWEEN 2000 AND 2009
AND id IN (SELECT rid FROM table2)
GROUP BY id, year
ORDER BY id, rate DESC
rendements:
year id rate
2006 p01 8
2003 p01 7.4
2008 p01 6.8
2001 p01 5.9
2007 p01 5.3
2009 p01 4.4
2002 p01 3.9
2004 p01 3.5
2005 p01 2.1
2000 p01 0.8
2001 p02 12.5
2004 p02 12.4
2002 p02 12.2
2003 p02 10.3
2000 p02 8.7
2006 p02 4.6
2007 p02 3.3
Ce que j'aimerais, c'est seulement les 5 premiers résultats pour chaque identifiant:
2006 p01 8
2003 p01 7.4
2008 p01 6.8
2001 p01 5.9
2007 p01 5.3
2001 p02 12.5
2004 p02 12.4
2002 p02 12.2
2003 p02 10.3
2000 p02 8.7
Existe-t-il un moyen de le faire en utilisant une sorte de modificateur de type LIMIT qui fonctionne dans le GROUP BY?
LIMIT
clause. Voici un article qui explique le problème en détail: Comment sélectionner la première / moins / max ligne par groupe dans SQL C'est un bon article - il présente une solution élégante mais naïve au problème "Top N par groupe", puis progressivement s'améliore.Réponses:
Vous pouvez utiliser la fonction agrégée GROUP_CONCAT pour regrouper toutes les années dans une seule colonne, regroupées
id
et triées parrate
:Résultat:
Et puis vous pouvez utiliser FIND_IN_SET , qui retourne la position du premier argument à l'intérieur du second, par exemple.
En utilisant une combinaison de
GROUP_CONCAT
etFIND_IN_SET
, et en filtrant par la position retournée par find_in_set, vous pouvez ensuite utiliser cette requête qui ne renvoie que les 5 premières années pour chaque id:Veuillez voir le violon ici .
Veuillez noter que si plusieurs lignes peuvent avoir le même taux, vous devriez envisager d'utiliser GROUP_CONCAT (taux DISTINCT ORDER BY) sur la colonne taux au lieu de la colonne année.
La longueur maximale de la chaîne renvoyée par GROUP_CONCAT est limitée, donc cela fonctionne bien si vous devez sélectionner quelques enregistrements pour chaque groupe.
la source
SET SESSION group_concat_max_len = <maximum length>;
dans le cas de l'OP, un non-problème (puisque la valeur par défaut est 1024), mais à titre d'exemple, group_concat_max_len doit être d'au moins 25: 4 (max longueur d'une chaîne d'année) + 1 (caractère séparateur), multiplié par 5 (5 premières années). Les chaînes sont tronquées plutôt que de générer une erreur, alors faites attention aux avertissements tels que1054 rows in set, 789 warnings (0.31 sec)
.FIND_IN_SET()
. J'ai essayéFIND_IN_SET() =2
mais ne montrant pas le résultat comme prévu.La requête d'origine utilisait des variables utilisateur et
ORDER BY
sur des tables dérivées; le comportement des deux bizarreries n'est pas garanti. Réponse révisée comme suit.Dans MySQL 5.x, vous pouvez utiliser le classement du pauvre sur la partition pour obtenir le résultat souhaité. Il suffit de joindre la table avec elle-même et pour chaque ligne, compter le nombre de lignes de moins qu'elle. Dans le cas ci-dessus, la ligne la plus petite est celle avec le taux le plus élevé:
Démo et résultat :
Notez que si les taux avaient des liens, par exemple:
La requête ci-dessus renverra 6 lignes:
Changez pour
HAVING COUNT(DISTINCT l.rate) < 5
obtenir 8 lignes:Ou changez pour
ON t.id = l.id AND (t.rate < l.rate OR (t.rate = l.rate AND t.pri_key > l.pri_key))
obtenir 5 lignes:Dans MySQL 8 ou version ultérieure, utilisez simplement les fonctions
RANK
,DENSE_RANK
ouROW_NUMBER
:la source
WHERE rank <=5
? Pour la première fois, je n'obtiens pas 5 lignes de chaque identifiant, mais après cela, je peux obtenir comme vous l'avez dit.SET
déclaration (voir première requête). Il est nécessaire.ORDER BY
dans la table dérivée peut, et sera souvent, ignoré. Cela bat l'objectif. On trouve ici des groupes efficaces .ORDER BY
dans des délivrances / sous-requêtes comme ça .. C'est la raison pour laquelle les versions modernes de MySQL / MariaDB ignorent lesORDER BY
sous -requêtes sans utiliserLIMIT
, je crois que les normes ANSI / ISO SQL 2008/2011/2016 rendent lesORDER BY
livraisons / sous-requêtes légales lors de leur utilisation en combinaison avecFETCH FIRST n ROWS ONLY
Pour moi quelque chose comme
marche parfaitement. Pas de requête compliquée.
par exemple: obtenez le top 1 pour chaque groupe
la source
Non, vous ne pouvez pas LIMITER les sous-requêtes de manière arbitraire (vous pouvez le faire dans une mesure limitée dans les nouveaux MySQL, mais pas pour 5 résultats par groupe).
Il s'agit d'une requête de type groupe maximum, ce qui n'est pas trivial à faire en SQL. Il existe différentes façons de résoudre ce problème, ce qui peut être plus efficace dans certains cas, mais pour le top-n en général, vous voudrez regarder la réponse de Bill à une question précédente similaire.
Comme avec la plupart des solutions à ce problème, il peut renvoyer plus de cinq lignes s'il existe plusieurs lignes avec la même
rate
valeur, vous devrez donc peut-être encore une quantité de post-traitement pour vérifier cela.la source
Cela nécessite une série de sous-requêtes pour classer les valeurs, les limiter, puis effectuer la somme lors du regroupement
la source
Essaye ça:
la source
La sous-requête est presque identique à votre requête. Seul le changement ajoute
la source
ROW_NUMBER()
).row_number()
est disponible .Créez les colonnes virtuelles (comme RowID dans Oracle)
table:
Les données:
SQL comme ceci:
si supprimez la clause where dans t3, cela se présente comme ceci:
GET "TOP N Record" -> ajouter le "rownum <= 3" dans la clause where (la clause where de t3);
CHOISIR "l'année" -> ajouter la "ENTRE 2000 ET 2009" dans la clause where (la clause where du t3);
la source
J'ai pris un peu de travail, mais je pense que ma solution serait quelque chose à partager car elle semble élégante et assez rapide.
Notez que cet exemple est spécifié aux fins de la question et peut être modifié assez facilement à d'autres fins similaires.
la source
Le message suivant: sql: sélection du premier enregistrement N par groupe décrit la manière compliquée d'y parvenir sans sous-requêtes.
Il améliore les autres solutions proposées ici par:
Ce n'est cependant pas joli. Une bonne solution serait réalisable si les fonctions de fenêtre (alias fonctions analytiques) étaient activées dans MySQL - mais elles ne le sont pas. L'astuce utilisée dans ce post utilise GROUP_CONCAT, qui est parfois décrit comme "les fonctions de fenêtre du pauvre pour MySQL".
la source
pour ceux comme moi qui ont expiré. J'ai fait ci-dessous pour utiliser les limites et tout le reste par un groupe spécifique.
il parcourt une liste de domaines et insère ensuite une limite de 200 chacun
la source
Essaye ça:
la source
Veuillez essayer la procédure stockée ci-dessous. J'ai déjà vérifié. J'obtiens un résultat correct mais sans l'utiliser
groupby
.la source