2009-07-07 2 views
2

Мои данные выглядит следующим образом:Как получить последние 2 элементов в каждой категории в одном выберите (с MySQL)

 
id|category|insertdate|title.... 
-------------------------------- 
1|1|123|test 1 
2|1|124|test 2 
3|1|125|test 3 
4|2|102|test 4 
5|2|103|test 5 
6|2|104|test 6 

То, что я пытаюсь выполнить это получить последние 2 записей в каждой категории (как для того, по insertdate DESC), поэтому результат должен быть:

 
id|.... 
---- 
3|.... 
2|.... 
6|.... 
5|.... 

Получение последней с помощью group by легко, но как я могу получить последние 2 без запуска нескольких запросов?

Спасибо за любую помощь ;-)
S.

ответ

3

Здесь вы идете приятель!

SET @counter = 0; 
SET @category = ''; 

SELECT 
    * 
FROM 
(
    SELECT 
     @counter := IF(data.category = @category, @counter+1, 0) AS counter, 
     @category := data.category, 
     data.* 
    FROM 
    (
     SELECT 
      * 
     FROM test 
     ORDER BY category, date DESC 
    ) data 
) data 
HAVING counter < 2 
+0

Это должно быть WHERE счетчик <2 вместо HAVING, но кроме того, что это идеальный ответ, если потенциально неэффективным, поскольку он должен пройти через каждую запись. – Welbog

+0

Я попытался использовать HAVING во втором подзапросе, но счетчик «еще не был установлен в этот момент» или «оптимизатор выполнил наличие перед @counter» ... поэтому, конечно, WHERE также выполнит работу во внешнем один. – 2009-07-07 12:39:52

0

Вы не собираетесь быть в состоянии сделать этот вид запроса в одном ЗЕЬЕСТ, но вы можете обернуть его в одну хранимую процедуру, которая возвращает один набор данных, добавляя результаты подзапросов во временную таблицу для каждой категории, а затем возвращая содержимое таблицы temp.

псевдокод:

Create a temp table 
For each distinct category, 
    Add the last two records to the temp table 
Return the temp table 

Вы будете в конечном итоге с набором данных, которые вы хотите, в конце концов, и с точки зрения приложения был сделан только один запрос.

0

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

select group_concat(id order by insertdate desc separator ','), category from tablename group by category 

или с использованием суб-выбирает (это на MySQL)

select category, 
    (select id from test as test1 where test1.category = test.category order by insertdate desc limit 0,1) as recent1, 
    (select id from test as test1 where test1.category = test.category order by insertdate desc limit 1,1) as recent2 
from test 
group by category; 

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

7

Это сложная проблема в SQL, которая лучше всего отвечает направляя вас к превосходной статье углубленного покрывающей вопрос: How to select the first/least/max row per group in SQL. Он охватывает специфические для MySQL средства для этого, а также общие методы.

+3

Отличный ресурс! Гораздо более исчерпывающий ответ с более ясными деталями. –

0
SELECT * 
FROM category AS c1 
WHERE (
    SELECT COUNT(c2.id) 
    FROM category AS c2 
    WHERE c2.id = c1.id AND c2.insertdate > c1.insertdate 
) < 2 
Смежные вопросы