Нахождение наибольших значений n каждой группы в MySQL - PullRequest
3 голосов
/ 01 июля 2011

У меня есть некоторые данные, отформатированные следующим образом:

Lane         Series
1            680
1            685
1            688
2            666
2            425
2            775
...

И я хотел бы получить наибольшее n серий на линию (скажем, 2 для примера, но это может быть гораздо большечем это)

Таким образом, результат должен быть:

Lane         Series
1            688
1            685
2            775
2            666

Получить самую высокую серию на линию очень просто, но я не могу найти способ получить 2 самых высоких результата.

Я использую агрегатную функцию MAX с GROUP BY, чтобы получить MAX, но нет функции "TOP N", как в SQL Server, и использование ORDER BY ... LIMIT возвращает только самые высокие N результатов в целом, а нена линию.

Поскольку я использую приложение JAVA, я закодировал себя, чтобы запросить базу данных и выбрать, что такое N, я мог бы сделать цикл и использовать LIMIT и циклически проходить по каждой линии, каждый раз делая разные запросы,но я хочу научиться делать это с помощью MySQL.

Ответы [ 4 ]

5 голосов
/ 01 июля 2011

Смотрите мой другой ответ для MySQL-только, но очень быстрого решения.

Это решение позволяет указать любое количество верхних строк на линию и не использует какой-либо "фанки" синтаксис MySQL - оно должно работать в большинстве баз данных.

select lane, series
from lane_series ls
group by lane, series
having (
    select count(*) 
    from lane_series
    where lane = ls.lane
    and series > ls.series) < 2 -- Here's where you specify the number of top rows
order by lane, series desc;

Тестовый вывод:

create table lane_series (lane int, series int);

insert into lane_series values 
(1, 680),
(1, 685),
(1, 688),
(2, 666),
(2, 425),
(2, 775);

select lane, series
from lane_series ls
group by lane, series
having (select count(*) from lane_series where lane = ls.lane and series > ls.series) < 2
order by lane, series desc;

+------+--------+
| lane | series |
+------+--------+
|    1 |    688 |
|    1 |    685 |
|    2 |    775 |
|    2 |    666 |
+------+--------+
4 rows in set (0.00 sec)
3 голосов
/ 01 июля 2011

Это решение является самым быстрым для MySQL и будет работать с очень большими таблицами, но оно использует "прикольные" функции MySQL, поэтому не будет использоваться для других разновидностей баз данных.

(отредактировано для сортировки до применения логики)

set @count:=-1, @lane:=0; 
select lane, series
from (select lane, series from lane_series order by lane, series desc) x
where if(lane != @lane, @count:=-1, 0) is not null
and if(lane != @lane, @lane:=lane, lane) is not null
and (@count:=@count+1) < 2; -- Specify the number of row at top of each group here

Чтобы поставить этот запрос на стероиды, определите индекс для дорожки и ряда: CREATE INDEX lane_series_idx on lane_series(lane, series);, и он будет выполнять (сверхбыстрое) сканирование только по индексу, чтобы другие текстовые столбцы не влияли на него.

Хорошие моменты этого запроса:

  1. Требуется только один проход таблицы (хотя и отсортированный)
  2. Он обрабатывает связи на любом уровне, например, если есть связь для 2-го уровня, будет отображаться только один от 2-го уровня - то есть количество строк является абсолютным и никогда не будет превышено

Вот тестовый вывод:

create table lane_series (lane int, series int);

insert into lane_series values (1, 680),(1, 685),(1, 688),(2, 666),(2, 425),(2, 775);

-- Execute above query:

+------+--------+
| lane | series |
+------+--------+
|    1 |    688 |
|    1 |    685 |
|    2 |    775 |
|    2 |    666 |
+------+--------+
2 голосов
/ 01 июля 2011

Это сработает, если вы знаете, что никогда не будете иметь связи за первое место:

SELECT lane,MAX(series)
FROM scores
GROUP BY lane
UNION 
SELECT s.lane,MAX(s.series)
FROM scores AS s
JOIN (
    SELECT lane,MAX(series) AS series
    FROM scores
    GROUP BY lane
) AS x ON (x.lane = s.lane)
WHERE s.series <> x.series
GROUP BY s.lane;
0 голосов
/ 02 июля 2011

Я думаю, что общий ответ @ Bohemian также можно записать как объединение, а не подзапрос, хотя, вероятно, это не имеет большого значения:

select ls1.lane, ls1.series
from lane_series ls1 left join lane_series ls2 on lane
where ls1.series < ls2.series
group by ls1.lane, ls1.series
having count(ls2.series) < 2 -- Here's where you specify the number of top rows
order by ls1.lane, ls1.series desc;
...