192 votes

Obtenez les n premiers enregistrements pour chaque groupe de résultats groupés

Ce qui suit est la plus simple possible exemple, bien que toute solution devrait être en mesure à l'échelle par le nombre n d'obtenir les meilleurs résultats sont nécessaires:

Étant donné un tableau comme celui ci-dessous, avec la personne, le groupe et l'âge des colonnes, comment voulez-vous obtenir les 2 plus anciens de personnes dans chaque groupe? (Les liens au sein de groupes ne doivent pas céder plus de résultats, mais de donner les 2 premiers dans l'ordre alphabétique)

+--------+-------+-----+
| Personne | Groupe | Âge |
+--------+-------+-----+
| Bob| 1 | 32 |
| Jill| 1 | 34 |
| Shawn| 1 | 42 |
| Jake| 2 | 29 |
| Paul| 2 | 36 |
| Laura| 2 | 39 |
+--------+-------+-----+

Résultats souhaités:

+--------+-------+-----+
| Shawn| 1 | 42 |
| Jill| 1 | 34 |
| Laura| 2 | 39 |
| Paul| 2 | 36 |
+--------+-------+-----+

REMARQUE: Cette question s'appuie sur un précédent- Obtenir des enregistrements avec valeur maximale pour chaque groupe d'regroupés les résultats SQL - pour l'obtention d'une seule rangée du haut, de chaque groupe, et qui a reçu une grande spécifique à MySQL réponse de @Bohème:

select * 
from (select * from mytable order by `Group`, Age desc, Person) x
group by `Group`

Aimerais être en mesure de construire ce, bien que je ne vois pas comment.

106voto

bluefeet Points 105508

Voici une façon de le faire, à l'aide de UNION ALL (Voir SQL jouer avec la Démo). Cela fonctionne avec les deux groupes, si vous avez plus de deux groupes, alors vous devez spécifier l' group nombre et ajouter des requêtes pour chaque group:

(
  select *
  from mytable 
  where `group` = 1
  order by age desc
  LIMIT 2
)
UNION ALL
(
  select *
  from mytable 
  where `group` = 2
  order by age desc
  LIMIT 2
)

Il existe une variété de façons de le faire, voir cet article pour déterminer le meilleur itinéraire pour votre situation:

http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/

Edit:

Cela pourrait fonctionner pour vous aussi, il génère un numéro de ligne pour chaque enregistrement. À l'aide d'un exemple à partir du lien ci-dessus vous obtiendrez seulement les enregistrements avec un numéro de ligne est inférieure ou égale à 2:

set @num := 0, @group := '';

select person, `group`, age
from 
(
   select person, `group`, age,
      @num := if(@group = `group`, @num + 1, 1) as row_number,
      @group := `group` as dummy
  from mytable
  order by `Group`, Age desc, person
) as x 
where x.row_number <= 2;

Voir SQL jouer avec la Démo

83voto

Mark Byers Points 318575

Dans d'autres bases de données que vous pouvez faire cela en utilisant ROW_NUMBER. MySQL ne supporte pas d' ROW_NUMBER mais vous pouvez utiliser des variables pour s'en inspirer:

SELECT
    person,
    groupname,
    age
FROM
(
    SELECT
        person,
        groupname,
        age,
        @rn := IF(@prev = groupname, @rn + 1, 1) AS rn,
        @prev := groupname
    FROM mytable
    JOIN (SELECT @prev := NULL, @rn := 0) AS vars
    ORDER BY groupname, age DESC, person
) AS T1
WHERE rn <= 2

Voir en ligne: sqlfiddle


Edit je viens de remarquer que bluefeet posté un très semblables réponse: +1 pour lui. Cependant cette réponse a deux petits avantages:

  1. Il est d'une seule requête. Les variables sont initialisées à l'intérieur de l'instruction SELECT.
  2. Il gère les liens comme décrit dans la question (en ordre alphabétique par nom).

Donc je vais le laisser ici au cas où ça pourrait aider quelqu'un.

57voto

snuffn Points 1252

Essaye ça:

 SELECT a.person, a.group, a.age FROM person AS a WHERE 
(SELECT COUNT(*) FROM person AS b 
WHERE b.group = a.group AND b.age >= a.age) <= 2 
ORDER BY a.group ASC, a.age DESC
 

DEMO

10voto

Travesty3 Points 9651

Regarde ça:

 SELECT
  p.Person,
  p.`Group`,
  p.Age
FROM
  people p
  INNER JOIN
  (
    SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`
    UNION
    SELECT MAX(p3.Age) AS Age, p3.`Group` FROM people p3 INNER JOIN (SELECT MAX(Age) AS Age, `Group` FROM people GROUP BY `Group`) p4 ON p3.Age < p4.Age AND p3.`Group` = p4.`Group` GROUP BY `Group`
  ) p2 ON p.Age = p2.Age AND p.`Group` = p2.`Group`
ORDER BY
  `Group`,
  Age DESC,
  Person;
 

Violon SQL: http://sqlfiddle.com/#!2/cdbb6/15

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X