Domanda

Dato questo set di dati:

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

Devo trovare le 3 persone più anziane, ma solo una di ogni città.

Se fosse solo i tre più vecchi, sarebbe ...

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

Tuttavia, dal momento che sia Egon Spengler che Mac Taylor si trovano a New York, Egon Spengler abbandonerà e al suo posto entrerà quello successivo (Sarah Connor / Los Angeles).

Qualche soluzione elegante?

Aggiornamento:

Attualmente una variante di PConroy è la soluzione migliore / più veloce:

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;

La sua query originale con " IN " è estremamente lento con set di dati di grandi dimensioni (interrotto dopo 5 minuti), ma spostando la subquery su un JOIN si accelera molto. Ci sono voluti circa 0,15 secondi per ca. 1 mio file nel mio ambiente di test. Ho un indice su " City, Birthyear " e un secondo solo su " Birthyear " ;.

Nota: si riferisce a ...

È stato utile?

Soluzione

Probabilmente non è la soluzione più elegante e le prestazioni di IN possono risentire di tavoli più grandi.

La query nidificata ottiene il Birthyear minimo per ogni città. Solo i record che hanno questo Birthyear sono abbinati nella query esterna. Ordinando per età e limitando a 3 risultati ottieni le 3 persone più anziane che sono anche le più anziane nella loro città (Egon Spengler abbandona ..)

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  |
+-----------------+-------------+------+----+

Modifica - aggiunto Raggruppa per città alla query esterna, poiché le persone con gli stessi anni di nascita restituirebbero più valori. Il raggruppamento sulla query esterna garantisce che verrà restituito un solo risultato per città, se più di una persona ha quel Birthyear minimo. La colonna ct mostrerà se in città esiste più di una persona con quel Birthyear

Altri suggerimenti

Probabilmente questa non è la soluzione più elegante e veloce, ma dovrebbe funzionare. Non vedo l'ora di vedere le soluzioni dei veri guru del database.

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

Qualcosa del genere?

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)

Non carino ma dovrebbe funzionare anche con più persone con lo stesso dob:

Dati del 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

Domanda:

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

AGGIORNAMENTO ho appena scoperto che è utile utilizzare USING anziché ON. rimuoverà le colonne duplicate nel risultato.

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;

POSTO ORIGINALE

ciao, ho provato ad usare la tua query aggiornata ma stavo ottenendo risultati sbagliati fino a quando non ho aggiunto ulteriori condizioni per partecipare (anche una colonna in più in selezione selezione). trasferito alla tua query, sto usando questo:

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;

in teoria non dovresti aver bisogno dell'ultimo GROUP BY P.City, ma l'ho lasciato lì per ora, per ogni evenienza. probabilmente lo rimuoverà più tardi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top