Как выбрать одним запросом 5 последних записей каждой категории в MySQL?

Здравствуйте.

Допустим у нас есть таблица следующей структуры: id, cid, title.

Может есть какой-нибудь элегантный способ выбрать одним запросом 5 последних записей каждой категории(cid)?
  • Вопрос задан
  • 43847 просмотров
Решения вопроса 1
Пригласить эксперта
Ответы на вопрос 5
kozlice
@kozlice
Пробуем. Все замеры на таблице с 5000 записей, рандомным cid от 1 до 10, id primary, на cid индекс.

На PostgreSQL можно запрашивать
SELECT a.*
FROM somedata AS a
WHERE a.id IN (
	SELECT b.id
	FROM somedata AS b
	WHERE b.cid = a.cid
	ORDER BY b.id DESC
	LIMIT 5
)
ORDER BY a.cid DESC, a.id DESC

Но запрос медленный (0.125 сек), и время выполнения растёт прямо пропорционально кол-ву записей и категорий. MySQL вообще не поддерживает LIMIT во вложенных запросах, идём дальше. Мастерим страшного монстра:
SELECT a.*
FROM somedata AS a
WHERE a.id IN (
	SELECT id
	FROM somedata AS b
	WHERE b.cid = a.cid AND (SELECT COUNT(*) FROM somedata AS c WHERE c.id >= b.id AND c.cid = b.cid) <= 5
)
ORDER BY a.cid DESC, a.id DESC

Он, конечно, справился, получены верные данные, но выполнение такого запроса заняло… 15.87 сек. Не хотеть, правда? :)

Самым производительным оказалось эстетически уродливое уродливое решение. Склеиваем в PHP запросы для каждой категории в единый с помощью UNION:
(SELECT * FROM somedata WHERE cid = 1 ORDER BY id DESC LIMIT 5)
UNION
(SELECT * FROM somedata WHERE cid = 2 ORDER BY id DESC LIMIT 5)
UNION
(SELECT * FROM somedata WHERE cid = 3 ORDER BY id DESC LIMIT 5)

и т.д. до cid = 10, и о чудо: запрос выполняется на MySQL за 0.002 сек, выдавая нужный результат.

Хотя, возможно я упустил какое-то очевидное решение с приемлемой производительностью. Если найдёте – расскажите нам :)
Ответ написан
Zazza
@Zazza
order by `id` desc limit 5?
Ответ написан
А что считаем первой, что последней? id?
Ответ написан
@phasma
Попробуйте:

SELECT `t1`.`id`, `t1`.`cid`, `t1`.`title`, COUNT(*) as `counter` FROM `test` `t1` JOIN `test` `t2` ON `t1`.`cid` = `t2`.`cid` AND `t1`.`id` >= `t2`.`id` GROUP BY `t1`.`cid`, `t1`.`id` HAVING `counter` <= 5 ORDER BY `t1`.`cid`, `t1`.`id`;
Ответ написан
@kenga
5 селектов и 1 запрос, включающий в себя 5 селектов с UNION ALL будет самым лучшим решением для рабочего проекта.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы