Question

Étant donné cet ensemble de données:

ID  Name            City            Birthyear
1   Egon Spengler   New York        1957
2   Mac Taylor      New York        1955
3   Sarah Connor    Los Angeles     1959
4   Jean-Luc Picard La Barre        2305
5   Ellen Ripley    Nostromo        2092
6   James T. Kirk   Riverside       2233
7   Henry Jones     Chicago         1899

Je dois trouver les 3 personnes les plus âgées, mais une seule par ville.

Si ce ne sont que les trois plus anciens, ce serait ...

  • Henry Jones / Chicago
  • Mac Taylor / New York
  • Egon Spengler / New York

Cependant, puisque Egon Spengler et Mac Taylor sont tous deux situés à New York, Egon Spengler abandonnerait et le prochain (Sarah Connor / Los Angeles) entrerait à la place.

Des solutions élégantes?

Mise à jour:

Actuellement, une variante de PConroy est la solution la meilleure / la plus rapide:

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 ON P2.Birthyear = P.Birthyear
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

Sa requête initiale avec " IN " est extrêmement lent avec de grands ensembles de données (abandonné au bout de 5 minutes), mais le déplacement de la sous-requête vers un JOIN accélérera beaucoup. Il a fallu environ 0,15 seconde pour env. 1 mio de lignes dans mon environnement de test. J'ai un index sur "Ville, année de naissance". et un deuxième juste sur "Birthyear".

Remarque: cela est lié à ...

Était-ce utile?

La solution

Ce n’est probablement pas la solution la plus élégante, et les performances de IN risquent de pâtir des tables plus volumineuses.

La requête imbriquée obtient le Année de naissance minimum pour chaque ville. Seuls les enregistrements ayant ce Birthyear sont mis en correspondance dans la requête externe. En classant par âge puis en limitant les 3 résultats obtenus, vous obtenez les 3 personnes les plus âgées qui sont aussi les plus âgées de leur ville (Egon Spengler abandonne ..)

SELECT Name, City, Birthyear, COUNT(*) AS ct
FROM table
WHERE Birthyear IN (SELECT MIN(Birthyear)
               FROM table
               GROUP by City)
GROUP BY City
ORDER BY Birthyear DESC LIMIT 3;

+-----------------+-------------+------+----+
| name            | city        | year | ct |
+-----------------+-------------+------+----+
| Henry Jones     | Chicago     | 1899 | 1  |
| Mac Taylor      | New York    | 1955 | 1  |
| Sarah Connor    | Los Angeles | 1959 | 1  |
+-----------------+-------------+------+----+

Modifier : ajout de GROUP BY City à la requête externe, car les personnes ayant la même année de naissance renverraient plusieurs valeurs. Le regroupement sur la requête externe garantit qu'un seul résultat sera renvoyé par ville, si plusieurs personnes ont ce minimum Année de naissance . La colonne ct indiquera si plus d'une personne existe dans la ville avec ce Birthyear

Autres conseils

Ce n’est probablement pas la solution la plus élégante et la plus rapide, mais cela devrait fonctionner. Je suis impatient de voir les solutions de vrais gourous de la base de données.

select p.* from people p,
(select city, max(age) as mage from people group by city) t
where p.city = t.city and p.age = t.mage
order by p.age desc

Quelque chose comme ça?

SELECT
  Id, Name, City, Birthyear
FROM
  TheTable
WHERE
  Id IN (SELECT TOP 1 Id FROM TheTable i WHERE i.City = TheTable.City ORDER BY Birthyear)

Ce n’est pas beau, mais vous devriez aussi travailler avec plusieurs personnes ayant le même dob:

Données de test:

select id, name, city, dob 
into people
from
(select 1 id,'Egon Spengler' name, 'New York' city , 1957 dob
union all select 2, 'Mac Taylor','New York', 1955
union all select 3, 'Sarah Connor','Los Angeles', 1959
union all select 4, 'Jean-Luc Picard','La Barre', 2305
union all select 5, 'Ellen Ripley','Nostromo', 2092
union all select 6, 'James T. Kirk','Riverside', 2233
union all select 7, 'Henry Jones','Chicago', 1899
union all select 8, 'Blah','New York', 1955) a

Requête:

select 
    * 
from 
    people p
    left join people p1
    ON 
        p.city = p1.city
        and (p.dob > p1.dob and p.id <> p1.id)
        or (p.dob = p1.dob and p.id > p1.id)
where
    p1.id is null
order by 
    p.dob

@BlaM

MISE À JOUR Je viens de constater qu'il est bon d'utiliser USING au lieu de ON. cela supprimera les colonnes en double dans le résultat.

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT City, MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 USING(Birthyear, City)
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

POST ORIGINAL

salut, j’ai essayé d’utiliser votre requête mise à jour, mais j’obtenais des résultats erronés jusqu’à ce que j’ai ajouté une condition supplémentaire pour rejoindre (également une colonne supplémentaire dans la sélection de jointure). transféré à votre requête, je l'utilise:

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT City, MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 ON P2.Birthyear = P.Birthyear AND P2.City = P.City
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

en théorie, vous n’auriez pas besoin de la dernière version de GROUP BY P.City, mais je l’ai laissée là pour le moment, juste au cas où. le supprimera probablement plus tard.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top