SQL выбрать n-го члена группы - PullRequest
23 голосов
/ 20 января 2009

Если у меня есть таблица USER, такая как

class | age
--------------
1       20    
3       56
2       11
1       12
2       20

Тогда я могу легко получить самого молодого пользователя в каждом классе с помощью

select class, min(age)
from   user
group by class;

Аналогично, заменяя min на max, я могу получить самое старое. Но как я могу получить 10-й самый младший (или самый старый) в каждом классе ? Кстати, я использую MySql v.5.0.

Приветствия

Ответы [ 6 ]

19 голосов
/ 21 января 2009
SELECT a.class,
(
    SELECT b.age 
    FROM users b 
    WHERE b.class = a.class
    ORDER BY age 
    LIMIT 1,1
) as age
FROM users a
GROUP BY a.class

Получил бы 2-го младшего в каждом классе. Если бы вы хотели 10-го младшего, вы бы сделали LIMIT 9,1, а если бы вы хотели 10-го старшего, вы бы сделали ORDER BY age DESC.

9 голосов
/ 14 мая 2013

Здесь N представляет Nth запись oldest

SELECT *
FROM users k
WHERE N = (SELECT
             COUNT( DISTINCT age)
           FROM users u
           WHERE k.age >= u.age
               AND k.class = u.class
           GROUP BY u.class)

и это дает Nth запись youngest

SELECT *
FROM users k
WHERE N = (SELECT
             COUNT(DISTINCT age)
           FROM users u
           WHERE k.age <= u.age
               AND k.class = u.class
           GROUP BY u.class)
2 голосов
/ 21 января 2009

Единственный независимый способ sql (даже если у вас нет подзапросов mysql <5) </p>

 select  u1.class, u1.age, count(*)  from      user u1 join user u2 
 on u1.class = u2.class and u1.age >= u2.age
 group by u1.class, u1.age
 having count(*) = [number]

получает вас [число] старейших в классе

 select  u1.class, u1.age, count(*)  from      user u1 join user u2 
 on u1.class = u2.class and u1.age <= u2.age
 group by u1.class, u1.age
 having count(*) = [number]

получает вас [число] младших в классе

Если два человека имеют одинаковый возраст, это может не сработать, так как оба возвращаются. Если вы хотите вернуть только один из них, вам понадобится уникальный ключ, и запрос будет более сложным.

0 голосов
/ 21 января 2009

Любой ответ, который присоединяется к таблице сам по себе, создаст закон квадрата ...

- a JOIN b ON a.class = b.class AND a.age >= b.age  
- on average the >= condition will be true for half the class  

- 6 people in a class  
->6*6/2 = 18

- 10 people in a class
->10*10/2 = 50

-> very rapid growth

По мере увеличения размеров таблицы производительность будет быстро ухудшаться. Если вы держите вещи маленькими, и они не будут сильно расти, это проблема? Твой звонок там ...

Альтернатива включает в себя больше кода, но растет линейно ...

  • Сначала вставьте все записи в новую таблицу с полем IDENTITY, упорядоченным по классу, а затем по возрасту
  • Теперь для каждого класса найдите МИН (id)
  • Теперь для каждого класса промыть запись где = MIN (id) + 8 (для 9-го старшего)

Есть много способов сделать последние 2 шага. Я бы лично использовал ...

SELECT
    [USER_WITH_IDS].id,
    [USER_WITH_IDS].class,
    [USER_WITH_IDS].age
FROM
    [USER_WITH_IDS]
WHERE
    [USER_WITH_IDS].id = (
                          SELECT
                              MIN([min].ID) + 8
                          FROM
                              [USER_WITH_IDS] AS [min]
                          WHERE
                              [min].class = [USER_WITH_IDS].class
                         )

Что это дает ...

  • Один проход для создания новых идентификаторов
  • Один проход, чтобы получить МИН (id) для каждого класса
  • Один проход, чтобы получить нужные записи

  • И в зависимости от того, насколько хорош оптимизатор, использование индекса (class, id) позволит ему объединить последние 2 прохода в 1 проход.

2 или 3 прохода, независимо от размера стола или размера класса. Линейный, а не квадратный закон ...

0 голосов
/ 21 января 2009
 SELECT 
     userid,  
     class,  
     age,  
     (    SELECT COUNT(1) FROM user  
          WHERE class = c.class  AND age > u.age  
     ) AS oldercount  
FROM user AS u  
WHERE oldercount = 9
GROUP BY class

или

 SELECT userid,  
         class,  
         age  
  FROM user AS u    
  WHERE (SELECT COUNT(1) FROM class WHERE class = c.class AND age > u.age) = 9  
GROUP BY class
0 голосов
/ 20 января 2009

В SQL Server это довольно просто:

select 
  *
from(
   select 
      *,
      row_number() over(order by age asc) as eldest
   from class order by age asc) a
where a.eldest = 10

Следуя этой схеме, для MySQL, я думаю, вы захотите взглянуть на это: http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...