2008-10-10 7 views
34

Учитывая этот набор данных:MySQL: Выберите N строк, но только уникальных значений в одном столбце

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 

мне нужно найти 3 самых старых людей, но только один из каждого города.

Если бы только быть три старых, было бы ...

  • Генри Джонс/Чикаго
  • Mac Taylor/Нью-Йорк
  • Эгон Шпенглер/Нью-Йорк

Однако, так как Эгон Шпенглер и Мак Тейлор находятся в Нью-Йорке, Эгон Шпенглер выпадет, а следующая (Сара Коннор/Лос-Анджелес) появится вместо него.

Любые элегантные решения?

Update:

В настоящее время изменение PConroy является лучшим/быстрым решением:

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; 

Его первоначальный запрос с "IN" экстремально медленно с большими наборами данных (прерывается через 5 минут) , но перемещение подзапроса в JOIN ускорит его. Это заняло около 0,15 секунды прибл. 1 mio строк в моей тестовой среде. У меня есть указатель на «Город, Рождение», а второй - на «Рождение».

Примечание: Это связано с ...

+0

Примечание: Это не настоящая проблема, которую я должен решить, но пример. Мне нужно решение для двух разных заданий: а) найти самую высокую цену в каждой комнате - если несколько с одинаковой ценой: получите самую новую. б) получить 10 заданий из очереди (упорядочено по приоритету), но только для каждого клиента. – BlaM 2008-10-10 11:07:12

ответ

18

Наверное не самых элегантных решений и производительность IN могут страдать от больших таблиц.

Вложенный запрос получает минимум Birthyear для каждого города. Только записи, у которых есть этот Birthyear, сопоставляются во внешнем запросе. Сортировка по возрасту, то ограничение на 3 результаты получает вас 3 самых старых людей, которые также является старейшим в своем городе (Эгон Шпенглер выпадает ..)

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

Редактировать - добавил GROUP BY City для внешнего запроса, так как люди с те же годы рождения возвратят несколько значений. Группировка по внешнему запросу гарантирует, что только один результат будет возвращен по городу, если более одного человека имеет этот минимум Birthyear.Колонка ct покажет, если более чем один человек существует в городе с тем, что Birthyear

2

Что-то вроде этого?

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

Это T-SQL, я знаю. Должна быть легко адаптирована к MySQL или любому другому диалекту. – Tomalak 2008-10-10 10:30:10

+0

MySQL не поддерживает TOP/LIMIT в «IN» -Subqueries – BlaM 2008-10-10 10:41:15

3

Это, вероятно, не самое элегантное и быстрое решение, но оно должно работать. Я с нетерпением жду решения реальных гуру базы данных.

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

Это ближе всего к тому, что я бы сделал, хотя я бы попытался представить идентификатор где-нибудь, потому что в том же городе могут быть два человека с одинаковым возрастом , – BlaM 2008-10-10 10:45:31

+0

BTW: В этом случае не имеет значения, какой из двух человек выбран. Это должно быть просто «всего одно». – BlaM 2008-10-10 10:47:50

1

Не очень, но должен также работать с несколькими людьми с тем же DOB:

Тест данных:

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 

запросов :

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 
1

@BlaM

ОБНОВЛЕНО только что нашел, что его полезно использовать USING вместо ON. он удалит дубликаты столбцов в результате.

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; 

ОРИГИНАЛЬНЫЙ ПОСТ

привет, я пытался использовать обновленный запрос, но я получаю неверные результаты, пока я не добавите дополнительное условие, чтобы присоединиться (также дополнительный столбец в присоединиться выберите). переданы на ваш запрос, i'am с помощью этого:

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; 

в теории вы не должны в прошлом GROUP BY P.City, но я оставил его там на данный момент, на всякий случай. вероятно, удалит его позже.

 Смежные вопросы

  • Нет связанных вопросов^_^