Сортировка MySQL по дате с GROUP BY

мой стол titles выглядит так

id |group|date                |title
---+-----+--------------------+--------
1  |1    |2012-07-26 18:59:30 | Title 1
2  |1    |2012-07-26 19:01:20 | Title 2
3  |2    |2012-07-26 19:18:15 | Title 3
4  |2    |2012-07-26 20:09:28 | Title 4
5  |2    |2012-07-26 23:59:52 | Title 5

мне нужен последний результат из каждой группы упорядочены по дате в порядке убывания. Что-то вроде этого!--6-->

id |group|date                |title
---+-----+--------------------+--------
5  |2    |2012-07-26 23:59:52 | Title 5
2  |1    |2012-07-26 19:01:20 | Title 2

пробовал

SELECT *
FROM `titles`
GROUP BY `group`
ORDER BY MAX( `date` ) DESC

но я получаю первые результаты от групп. Такой

id |group|date                |title
---+-----+--------------------+--------
3  |2    |2012-07-26 18:59:30 | Title 3
1  |1    |2012-07-26 19:18:15 | Title 1

что я делаю не так? Этот запрос будет сложнее, если я использую LEFT JOIN?

8 ответов


на этой странице было очень полезно для меня; он научил меня, как использовать self-joins, чтобы получить max/min/something-n строк в группе.

в вашей ситуации он может быть применен к эффекту, который вы хотите так:

SELECT * FROM
(SELECT group, MAX(date) AS date FROM titles GROUP BY group)
AS x JOIN titles USING (group, date);

Я нашел эту тему через Google, похоже у меня тот же вопрос. Вот мое собственное решение, если, как и я, вам не нравятся подзапросы:

-- Create a temporary table like the output
CREATE TEMPORARY TABLE titles_tmp LIKE titles;

-- Add a unique key on where you want to GROUP BY
ALTER TABLE titles_tmp ADD UNIQUE KEY `group` (`group`);

-- Read the result into the tmp_table. Duplicates won't be inserted.
INSERT IGNORE INTO titles_tmp
  SELECT *
    FROM `titles`
    ORDER BY  `date` DESC;

-- Read the temporary table as output
SELECT * 
  FROM titles_tmp
  ORDER BY `group`;

Он имеет лучшую производительность. Вот как увеличить скорость, если date_column имеет тот же порядок, что и auto_increment_one (тогда вам не нужен оператор ORDER BY):

-- Create a temporary table like the output
CREATE TEMPORARY TABLE titles_tmp LIKE titles;

-- Add a unique key on where you want to GROUP BY
ALTER TABLE titles_tmp ADD UNIQUE KEY `group` (`group`);

-- Read the result into the tmp_table, in the natural order. Duplicates will update the temporary table with the freshest information.
INSERT INTO titles_tmp
  SELECT *
    FROM `titles`

  ON DUPLICATE KEY 
    UPDATE  `id`    = VALUES(`id`), 
            `date`  = VALUES(`date`), 
            `title` = VALUES(`title`);

-- Read the temporary table as output
SELECT * 
  FROM titles_tmp
  ORDER BY `group`;

результат :

+----+-------+---------------------+---------+
| id | group | date                | title   |
+----+-------+---------------------+---------+
|  2 |     1 | 2012-07-26 19:01:20 | Title 2 |
|  5 |     2 | 2012-07-26 23:59:52 | Title 5 |
+----+-------+---------------------+---------+

на больших таблицах этот метод делает значительный момент с точки зрения производительности.


Ну, если даты уникальны в группе, это будет работать (если нет, вы увидите несколько строк, которые соответствуют максимальной дате в группе). (Кроме того, плохое именование столбцов, "группа", "дата" может дать вам синтаксические ошибки и такую специально "группу")

select t1.* from titles t1, (select group, max(date) date from titles group by group) t2
where t2.date = t1.date
and t1.group = t2.group
order by date desc

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

если вы можете жить с дополнительный столбец возвращается, что-то вроде этого будет работать:

SELECT IF(s.group = @prev_group,0,1) AS latest_in_group
     , s.id
     , @prev_group := s.group AS `group`
     , s.date
     , s.title
  FROM (SELECT t.id,t.group,t.date,t.title
          FROM titles t
         ORDER BY t.group DESC, t.date DESC, t.id DESC
       ) s
  JOIN (SELECT @prev_group := NULL) p
HAVING latest_in_group = 1
 ORDER BY s.group DESC

то, что это делает, упорядочивает все строки по group и date в порядке убывания. (Мы указываем DESC на всех Столбцах в порядке BY, в случае, если есть индекс на (group,date,id) что MySQL может сделать "обратное сканирование". Включение id столбец получает детерминированное (повторяющееся) поведение, в случае, когда существует более одной строки с последним date значение.) Это встроенное представление с псевдонимами s.

"трюк" мы используем, чтобы сравнить group значение group значением из предыдущей строки. Всякий раз, когда у нас есть другое значение, мы знаем, что мы начинаем "новую" группу, и что эта строка является "последней" строкой (у нас есть функция IF, возвращающая 1). В противном случае (когда значения группы совпадают), это не последняя строка (и у нас есть функция IF возвращает 0).

затем мы отфильтровываем все строки, у которых этого нет latest_in_group установить как 1.

можно удалить этот дополнительный столбец, обернув этот запрос (как встроенное представление) в другой запрос:

SELECT r.id
     , r.group
     , r.date
     , r.title
  FROM ( SELECT IF(s.group = @prev_group,0,1) AS latest_in_group
              , s.id
              , @prev_group := s.group AS `group`
              , s.date
              , s.title
           FROM (SELECT t.id,t.group,t.date,t.title
                   FROM titles t
                  ORDER BY t.group DESC, t.date DESC, t.id DESC
                ) s
           JOIN (SELECT @prev_group := NULL) p
         HAVING latest_in_group = 1
       ) r
 ORDER BY r.group DESC

если id поле является автоинкрементным полем, и можно с уверенностью сказать, что наибольшее значение id поле также является самым высоким значением для date любой группы, то это простое решение:

SELECT   b.*
FROM     (SELECT MAX(id) AS maxid FROM titles GROUP BY group) a
JOIN     titles b ON a.maxid = b.id
ORDER BY b.date DESC 

используйте ниже запрос mysql, чтобы получить последнюю обновленную/вставленную запись из таблицы.

SELECT * FROM 
(
  select * from `titles` order by `date` desc
) as tmp_table
group by `group`
order by `date` desc

используйте следующий запрос, чтобы получить самую последнюю запись из каждой группы

SELECT 
T1.* FROM
(SELECT 
    MAX(ID) AS maxID
FROM
    T2
GROUP BY Type) AS aux
    INNER JOIN
T2 AS T2 ON T1.ID = aux.maxID ;

где ID-это поле автоматического приращения, а Type-тип записей,которые вы хотите сгруппировать.


MySQL использует немое расширение GROUP BY что не является надежным, если вы хотите получить такие результаты, поэтому вы можете использовать

select id, group, date, title from titles as t where id = 
(select id from titles where group = a.group order by date desc limit 1);

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