【问题标题】:MySQL sorting by date with GROUP BYMySQL 使用 GROUP BY 按日期排序
【发布时间】:2012-07-27 21:20:52
【问题描述】:

我的桌子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

我需要按日期降序排列的每个组的最新结果。像这样的

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,这个查询会更复杂吗?

【问题讨论】:

  • 如果您只需要两列(例如 ID 及其最新时间戳),这可能有效:stackoverflow.com/a/4448536/722036。它比在包含数百万行的大表上使用子查询更快

标签: mysql group-by sql-order-by left-join max


【解决方案1】:

This page 对我很有帮助;它教会了我如何使用自联接来获得每组的最大/最小/某事-n 行。

在你的情况下,可以像这样应用到你想要的效果:

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

【讨论】:

    【解决方案2】:

    我通过 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 |
    +----+-------+---------------------+---------+
    

    在大型表上,这种方法在性能方面具有重要意义。

    【讨论】:

      【解决方案3】:

      好吧,如果日期在组中是唯一的,这将起作用(如果不是,您将看到与组中的最大日期匹配的几行)。 (此外,列的错误命名,“组”、“日期”可能会给您带来语法错误,尤其是“组”)

      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
      

      【讨论】:

        【解决方案4】:

        另一种方法是利用 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
        

        这样做是按groupdate 降序排列所有行。 (我们在 ORDER BY 中的所有列上指定 DESC,以防(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
        

        【讨论】:

          【解决方案5】:

          如果您的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 
          

          【讨论】:

            【解决方案6】:

            使用下面的 mysql 查询从表中获取最新更新/插入的记录。

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

            【讨论】:

              【解决方案7】:

              使用以下查询获取每个组的最新记录

              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 是您想要分组的记录类型。

              【讨论】:

                【解决方案8】:

                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);
                

                在此查询中,每次对每个组的表进行完整扫描,以便它可以找到最近的日期。我找不到任何更好的替代品。希望这会对某人有所帮助。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-07-16
                  • 1970-01-01
                  • 2012-01-15
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多