mysql 分组取最新的一条记录(整条记录)

方法:mysql取分组后最新的一条记录,下面两种方法.一种是先筛选 出最大和最新的时间,在连表查询.一种是先排序,然后在次分组查询(默认第一条),就是最新的一条数据了  
    #select * from t_assistant_article as a, (select max(base_id) as base_id, max(create_time) as create_time from t_assistant_article as b group by base_id ) as b where a.base_id=b.base_id and a.create_time = b.create_time  
    #select base_id,max(create_time), max(article_id) as article_id from t_assistant_article as b group by base_id   
    select * from (select * from t_assistant_article order by create_time desc) as a group by base_id order by create_time desc

来源:http://blog.youkuaiyun.com/swweb/article/details/11059037

mysql "group by"与"order by"的研究--分类中最新的内容 http://www.jb51.net/article/23969.htm


在使用mysql排序的时候会想到按照降序分组来获得一组数据,而使用order by往往得到的不是理想中的结果,那么怎么才能使用group by 和order by得到理想中的数据结果呢?

例如 有一个 帖子的回复表,posts( id , tid , subject , message ,  dateline ) ,

id为 自动增长字段, tid为该回复的主题帖子的id(外键关联),  subject 为回复标题, message 为回复内容, dateline 为回复时间,用UNIX 时间戳表示,

现在要求 选出 前十个来自不同主题的最新回复


SELECT * FROM posts GROUP BY  tid  LIMIT 10


这样一个sql语句选出来的并非你想要的 最新的回复,而是最早的回复,实际上是某篇主题的第一条回复记录!

也就是说 GROUP BY 语句没有排序,那么怎么才能让 GROUP 按照 dateline 倒序排列呢?加上 order by 子句?

看下面:

SELECT * FROM posts GROUP BY  tid  ORDER BY dateline DESC LIMIT 10


这条语句选出来的结果和上面的完全一样,不过把结果倒序排列了,而选择出来的每一条记录仍然是上面的记录,原因是 group by 会比 order by 先执行,这样也就没有办法将 group by 之前,也就是在分组之前进行排序了, 有网友会写出下面的sql 语句:


SELECT * FROM posts GROUP BY  tid DESC ORDER BY dateline DESC LIMIT 10


也就是说 在 GROUP BY 的字段 tid 后面加上递减顺序,这样不就可以取得分组时的最后回复了吗?这个语句执行结果会和上面的一模一样,这里加上 DESC 和ASC对执行结果没有任何影响!其实这是一个错误的语句,原因是GROUP BY 之前并没有排序功能,mysql 手册上面说,GROUP BY 时是按照某种顺序排序的,某种顺序到底是什么顺序?其实根本没有顺序,因为按照tid分组,其实也就是说,把tid相等的归纳到一个组,这样想的 话,GROUP BY tid DESC 可以认为是在按照 tid 分组的时候,按照tid进行倒序排列,这不扯吗,既然是按照tid分组,当然是tid相等的归到一组,而这时候按照tid倒叙还是升序有个P用!


于是有网友发明下面的语句:


SELECT * FROM posts GROUP BY  tid , dateline DESC ORDER BY dateline DESC LIMIT 10


心想这样我就可以在分组前按照  dateline 倒序排列了,其实这个语句并没有起到按照tid分组的作用,原因还是上面的,在group by 字段后加 desc 还是 asc 是错误的写法,而这种写法 网友本意是想 按照 tid 分组,并且在分组的时候按照 dateline排倒序!而实际这句相当于下面的写法:(去掉 GROUP BY 字段后面的 DESC)


SELECT * FROM posts GROUP BY  tid , dateline ORDER BY dateline DESC LIMIT 10


也就是说,按照 tid 和 dateline 联合分组,只有在记录tid和dateline 同时相等的时候才归纳到一组,这显然不可能, 因为 dateline 时间线基本上是唯一的!


有人写出下面的语句:


SELECT *,max(dateline) as max_line FROM posts GROUP BY  tid ORDER BY dateline DESC LIMIT 10


这条语句的没错是选出了最大发布时间,但是你可以对比一下 dateline 和 max_dateline 并不相等!(可能有相当的情况,就是分组的目标记录只有一条的时候!)


为什么呢?原因很简单,这条语句相当于是 在group by 以后选出 本组的最大的 发布时间!对分组没有起到任何影响!因为SELECT子句是最后执行的!

后来更有网友发明了下面的写法!


SELECT *,max(dateline) as max_line FROM posts GROUP BY  tid HAVING dateline=max(dateline) ORDER BY dateline DESC LIMIT 10


这条语句的预期结果和想象中的并不相同!因为你会发现,分组的结果中大量的记录没有了!为什么?因为 HAVING 是在分组的时候执行的,也就说:在分组的时候加上一个这样的条件:选择出来的 dateline 要和 本组最大的dateline 相等,执行的结果和下面的语句相同:


SELECT *,max(dateline) as max_line FROM posts GROUP BY  tid HAVING count(*)=1 ORDER BY dateline DESC LIMIT 10


看了这条sql语句是不是明白了呢?

dateline=max(dateline) 只有在分组中的记录只有一条的时候才成立,原因很明白吧!只有一条他才会和本组的最大发布时间相等阿,(默认dateline为不重复的值)


原因还是因为 group by 并没有排序功能,所有的这些排序功能只是错觉,所以你最终选出的 dateline 和max(dateline) 永远不可能相等,除非本组的记录只有一条!GROUP BY 在分组的时候,可能是一个一个来找的,发现有相等的tid,去掉,保留第一个发现的那一条记录,所以找出来的 记录永远只是按照默认索引顺序排列的!


那么说了这么多,到底有没有办法让 group by 执行前分组阿?有的 ,子查询阿!


最简单的 :


SELECT * FROM (SELECT * FROM posts ORDER BY dateline DESC) GROUP BY  tid ORDER BY dateline DESC LIMIT 10


也有网友利用自连接实现的 ,这样的效率应该比上面的子查询效率高,不过,为了简单明了,就只用这样一种了,GROUP BY没有排序功能,可能是mysql弱智的地方,也许是我还没有发现,

<think>好的,我现在需要解决的问题是,如何用MySQL的GROUP BY来获每个分组中时间字段最大的那条记录。用户提到参考了四个引用,我需要仔细看看这些引用的内容,然后结合自己的知识来解答。 首先,引用[1]提到在MySQL中使用GROUP BY分组并按时间排序每条记录中时间最大的一个。这里可能涉及到分组后如何保留最大时间的记录。引用[2]指出可以通过GROUP BY结合聚合函数和子查询来获每个组的最大最小值对应的记录。引用[3]建议先排序分组,这样可能更高效。引用[4]给出了一个多表分组的解决方案,先排序然后使用子查询分组。 现在,我需要把这些方法整理出来。首先,常见的做法可能是使用子查询来先找到每个分组的最大时间,然后再关联回原表。例如,先通过子查询得到每个组的最大时间,然后和原表进行内连接,这样就能得到对应的记录。但这种方法可能需要多次扫描表,效率可能不高。 另一个方法是使用内联的派生表,先对原表按时间降序排序,然后进行分组,这样每个分组的第一条记录就是时间最大的。不过,在MySQL中,这种方法的可靠性可能依赖于特定的模式设置,比如ONLY_FULL_GROUP_BY是否禁用。引用[3]提到这种方法,先排序分组,可能只需要一次排序,效率更高。 此外,引用[4]中的例子是先进行排序,然后在子查询中使用GROUP BY,这样可能利用了排序后的结果来获每个分组的最大记录。例如: SELECT w.* FROM ( SELECT a1.* FROM table1 a1 LEFT JOIN table2 a2 ON a1.a1id = a2.a2id ORDER BY a1.time DESC ) w GROUP BY w.group_id; 这里的思路是先对数据进行排序,然后分组每个组的第一个记录,即时间最大的。但需要注意的是,在MySQL中,当使用GROUP BY时,非聚合列的值可能是不确定的,这决于MySQL的模式设置。如果关闭了ONLY_FULL_GROUP_BY模式,这种方法可能有效,但在严格模式下可能报错。 另外,还可以使用窗口函数,比如ROW_NUMBER(),这在MySQL 8.0及以上版本支持。通过为每个分组内的记录按时间排序并编号,然后编号为1的记录。例如: SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY time DESC) AS rn FROM table ) t WHERE rn = 1; 这种方法更现代,也更直观,但需要MySQL版本支持窗口函数。 不过,用户可能使用的是较旧的MySQL版本,所以可能需要考虑兼容性。这时候可能需要使用子查询和JOIN的方法,或者先排序分组的方法。 总结一下,可能的解决方案包括: 1. 子查询+JOIN:先找到每个分组的最大时间,再JOIN原表获整条记录。 2. 排序分组:利用MySQL的GROUP BY特性,先排序分组一条。 3. 使用窗口函数(适用于MySQL 8.0+)。 现在需要将这些方法具体写成SQL语句,并说明各自的优缺点。同时,需要注意用户可能遇到的常见问题,比如如何处理多个记录具有相同最大时间的情况,以及性能优化的建议。 另外,引用[3]提到先排序分组可能更快,因为只需要一次排序。这可能在某些情况下有效,但需要确保MySQL的配置允许这种写法,否则可能会有非确定性的结果。 例如,方法二的SQL可能如下: SELECT t.* FROM ( SELECT * FROM your_table ORDER BY time DESC ) AS t GROUP BY t.group_id; 这种方法假设在子查询中排序后,GROUP BY会每个group_id的第一个记录,即时间最大的。但这种方法在严格模式下可能不可靠,因为除了group_id和time之外的其他列可能来自不同的行,导致不可预测的结果。 因此,更可靠的方法是使用子查询找到每个group_id的最大时间,然后JOIN原表来匹配: SELECT t1.* FROM your_table t1 INNER JOIN ( SELECT group_id, MAX(time) AS max_time FROM your_table GROUP BY group_id ) t2 ON t1.group_id = t2.group_id AND t1.time = t2.max_time; 这种方法可能更安全,但需要两次扫描表,可能影响性能,尤其是在大数据量的情况下。 另外,如果存在多个记录具有相同的最大时间,这种方法会返回所有符合条件的记录,而方法二可能只返回其中一个不确定的记录。 因此,需要根据具体需求选择方法,并考虑数据量和MySQL版本等因素。</think>### 分组每组时间最大记录的实现方法 在 MySQL 中,要获每个分组中时间字段最大值对应的完整记录,可以通过以下三种方法实现: --- #### **方法 1:子查询 + JOIN** **原理**:先通过子查询获每个分组的最大时间,再与原表关联匹配。 **SQL 示例**: ```sql SELECT t1.* FROM your_table t1 INNER JOIN ( SELECT group_id, MAX(time) AS max_time FROM your_table GROUP BY group_id ) t2 ON t1.group_id = t2.group_id AND t1.time = t2.max_time; ``` **优点**:结果准确,兼容所有 MySQL 版本。 **缺点**:需两次扫描表,性能可能受影响[^2][^4]。 --- #### **方法 2:先排序分组** **原理**:先按时间降序排序,再利用 `GROUP BY` 特性每组第一条记录。 **SQL 示例**: ```sql SELECT * FROM ( SELECT * FROM your_table ORDER BY time DESC ) AS sorted_table GROUP BY group_id; ``` **优点**:只需一次排序,性能较好[^3]。 **缺点**: - 若未关闭 `ONLY_FULL_GROUP_BY` 模式,可能报错。 - 非时间字段的值可能不唯一(决于 MySQL 优化策略)。 --- #### **方法 3:窗口函数(MySQL 8.0+)** **原理**:使用 `ROW_NUMBER()` 为每组记录按时间排序编号,筛选编号为 1 的记录。 **SQL 示例**: ```sql SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY time DESC) AS rn FROM your_table ) AS ranked_table WHERE rn = 1; ``` **优点**:语法简洁,结果明确。 **缺点**:仅支持 MySQL 8.0 及以上版本。 --- ### **性能优化建议** 1. **索引优化**:为 `group_id` 和 `time` 字段添加联合索引(如 `(group_id, time)`),可大幅提升查询速度[^4]。 2. **去重处理**:若存在多个相同最大时间的记录,可通过 `DISTINCT` 或调整排序规则去重。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值