Pergunta

Dado este conjunto de dados:

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

Eu preciso encontrar as três pessoas mais velhas, mas apenas uma de todas as cidades.

Se fosse apenas os três mais antigos, seria ...

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

No entanto, como Egon Spengler e Mac Taylor estão localizados em Nova York, Egon Spengler abandonaria e o próximo (Sarah Connor / Los Angeles) entraria em frente.

Alguma soluções elegantes?

Atualizar:

Atualmente, uma variação do PConroy é a melhor/mais rápida solução:

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;

Sua consulta original com "In" é extremamente lenta com grandes conjuntos de dados (abortados após 5 minutos), mas mover a subconsência para uma junção acelerará muito. Demorou cerca de 0,15 segundos por aprox. 1 mio linhas no meu ambiente de teste. Eu tenho um índice em "City, BirthYear" e um segundo apenas em "BirthYear".

Nota: Isso está relacionado a ...

Foi útil?

Solução

Provavelmente não é a mais elegante das soluções e o desempenho de IN pode sofrer em tabelas maiores.

A consulta aninhada recebe o mínimo Birthyear para cada cidade. Apenas registros que têm isso Birthyear são pareados na consulta externa. O pedido por idade e depois a limitação de 3 resultados oferece as três pessoas mais antigas que também são as mais antigas da cidade (Egon Spengler desiste ..)

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

Editar - adicionado GROUP BY City Para a consulta externa, como pessoas com os mesmos anos de nascimento retornariam vários valores. O agrupamento na consulta externa garante que apenas um resultado seja devolvido por cidade, se mais de uma pessoa tiver esse mínimo Birthyear. o ct A coluna mostrará se mais de uma pessoa existir na cidade com isso Birthyear

Outras dicas

Provavelmente não é a solução mais elegante e rápida, mas deve funcionar. Estou ansioso para ver as soluções de gurus do banco de dados reais.

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

Algo parecido?

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)

Não é bonito, mas deve funcionar também com várias pessoas com o mesmo DOB:

Dados de teste:

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

Consulta:

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

ATUALIZADAAcabei de descobrir que é bom usar em vez de em vez de. Ele removerá colunas duplicadas no resultado.

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;

Postagem original

Olá, tentei usar sua consulta atualizada, mas estava obtendo resultados errados até adicionar condições extras para participar (também coluna extra no seleção de junção). transferido para sua consulta, estou usando isso:

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;

Em teoria, você não deve precisar do último grupo por P.City, mas eu o deixei lá por enquanto, apenas por precaução. Provavelmente o removerá mais tarde.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top