Pregunta

Dado este conjunto de datos:

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

Necesito encontrar a las 3 personas más viejas, pero solo una de cada ciudad.

Si solo fueran los tres más antiguos, sería ...

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

Sin embargo, dado que tanto Egon Spengler como Mac Taylor se encuentran en Nueva York, Egon Spengler abandonaría y en su lugar entraría la siguiente (Sarah Connor / Los Ángeles).

¿Alguna solución elegante?

Update:

Actualmente, una variación de PConroy es la mejor / más rápida solución:

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;

Su consulta original con " IN " es extremadamente lento con grandes conjuntos de datos (abortados después de 5 minutos), pero mover la subconsulta a JOIN lo acelerará mucho. Tomó alrededor de 0,15 segundos durante aprox. 1 millón de filas en mi entorno de prueba. Tengo un índice en "Ciudad, año de nacimiento" y un segundo solo en "Año de nacimiento".

Nota: Esto está relacionado con ...

¿Fue útil?

Solución

Probablemente no sea la solución más elegante, y el rendimiento de IN puede verse afectado en tablas más grandes.

La consulta anidada obtiene el Birthyear mínimo para cada ciudad. Solo los registros que tienen este Birthyear coinciden en la consulta externa. Ordenar por edad y luego limitarlo a 3 resultados te da las 3 personas más viejas que también son las más antiguas de su ciudad (Egon Spengler abandona ...)

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 : se agregó GROUP BY City a la consulta externa, ya que las personas con los mismos años de nacimiento devolverían múltiples valores. La agrupación en la consulta externa asegura que solo se devolverá un resultado por ciudad, si más de una persona tiene ese mínimo Birthyear . La columna ct mostrará si existe más de una persona en la ciudad con ese Birthyear

Otros consejos

Probablemente esta no sea la solución más elegante y rápida, pero debería funcionar. Estoy deseando ver las soluciones de los gurús de bases de datos reales.

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 así?

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)

No es bonito, pero también debería funcionar con varias personas con el mismo dob:

Datos de prueba:

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

ACTUALIZADO acabo de descubrir que es bueno usar USING en lugar de ON. eliminará columnas duplicadas en el 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;

PUBLICACIÓN ORIGINAL

hola, he intentado usar tu consulta actualizada pero estaba obteniendo resultados incorrectos hasta que agregué una condición adicional para unirme (también una columna adicional en la selección de combinación). transferido a su consulta, estoy usando esto:

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 teoría no debería necesitar el último GROUP BY P.City, pero lo he dejado allí por ahora, por si acaso. probablemente lo eliminará más tarde.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top