Получить 2 последние сообщения для каждой категории - PullRequest
1 голос
/ 15 декабря 2009

Допустим, у меня есть 2 таблицы: blog_posts и категории. Каждое сообщение в блоге относится только к ОДНОЙ категории, поэтому между этими двумя таблицами в основном находится внешний ключ.

Я хотел бы получить 2 последних сообщения из каждой категории, возможно ли добиться этого за один запрос? GROUP BY сгруппирует все и оставит только один ряд в каждой категории. Но я хочу 2 из них.

Было бы легко выполнить запрос 1 + N (N = номер категории). Сначала найдите категории. А затем получить 2 сообщения из каждой категории.

Я считаю, что было бы довольно легко выполнить M запросов (M = количество сообщений, которые я хочу из каждой категории). Первый запрос выбирает первый пост для каждой категории (с группой по). Второй запрос извлекает второй пост для каждой категории. и т.д.

Мне просто интересно, есть ли у кого-нибудь лучшее решение для этого. Я не против сделать 1 + N запросов для этого, но для любопытства и общего знания SQL, это будет оценено!

Заранее спасибо, кто может помочь мне с этим.

Ответы [ 2 ]

3 голосов
/ 15 декабря 2009

Ознакомьтесь с этой статьей MySQL о том, как работать с N главными вещами в произвольно сложных группировках; это хорошие вещи. Вы можете попробовать это:

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

SELECT
  *
FROM
(
  SELECT
    @counter := IF(posts.category = @category, @counter + 1, 0) AS counter,
    @category := posts.category,
    posts.*
    FROM
      (
      SELECT
        *
        FROM test
        ORDER BY category, date DESC
      ) posts
) posts
HAVING counter < 2
3 голосов
/ 15 декабря 2009
SELECT  p.*
FROM    (
        SELECT  id,
                COALESCE(
                (
                SELECT  datetime
                FROM    posts pi
                WHERE   pi.category = c.id
                ORDER BY
                        pi.category DESC, pi.datetime DESC, pi.id DESC
                LIMIT 1, 1
                ), '1900-01-01') AS post_datetime,
                COALESCE(
                (
                SELECT  id
                FROM    posts pi
                WHERE   pi.category = c.id
                ORDER BY
                        pi.category DESC, pi.datetime DESC, pi.id DESC
                LIMIT 1, 1
                ), 0) AS post_id
        FROM    category c
        ) q
JOIN    posts p
ON      p.category <= q.id
        AND p.category >= q.id
        AND p.datetime >= q.post_datetime
        AND (p.datetime, p.id) >= (q.post_datetime, q.post_id)

Создайте индекс для posts (category, datetime, id), чтобы это было быстро.

Обратите внимание на хак p.category <= c.id AND p.category >= c.id: это заставляет MySQL использовать Range checked for each record, что более эффективно для индекса.

См. Эту статью в моем блоге для аналогичной проблемы:

...