Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > Mysql分组查询取最新

Mysql分组查询取最新的几种方案总结

作者:lianshanchi

在写报表功能时遇到一个需要根据用户id分组查询最新一条钱包明细数据的需求,下面这篇文章主要给大家总结介绍了关于Mysql分组查询取最新的几种方案,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

1.row_number函数 (mysql8以上支持)

示例:

SELECT *
FROM
( SELECT *, row_number ( ) over ( PARTITION BY 分组字段 ORDER BY 排序字段 DESC ) AS rn FROM 表 ) pca
WHERE
pca.rn = 1

解释:

1. rn字段:返回的是分组内的结果排序依次递增

2. 分组字段:进行分组的字段

3. 排序字段:需要取最新逻辑的字段

注意:

row_number函数得到的字段rn不能在表后面直接加where条件过滤,需要用select包装生成临时表pca 再进行取最新过滤
并且该函数只能在mysql8.0版本以上运行,5.7不支持该函数

2.子查询

示例:

SELECT *
FROM
表 a
WHERE
a.排序字段 = ( SELECT max( b.排序字段 ) FROM 表 b WHERE b.分组字段 = a.分组字段 )
GROUP BY
a.分组字段

解释:

分组字段:进行分组的字段排序字段:严格意义上不叫排序字段,可以在当前sql语句中可以认为是在分组后需要找最新值字段

注意:

该子查询性能属于四种方案中最低,不适合大数据量查询使用,

最后GROUP BY分组字段是为了在多个最大值有多个相同数据情况下去重处理 按业务场景可去掉

3.临时表

示例:

创建临时表
CREATE TEMPORARY TABLE tmp_表 as select 分组字段,max(排序字段)as 排序字段 from 表 group by 排序字段;
关联临时表查询
select * from 表 p join tmp_表 tmp on tmp.分组字段= p.分组字段 and tmp.排序字段= p.排序字段

解释:

分组字段:进行分组的字段

排序字段:严格意义上不叫排序字段,可以在当前sql语句中可以认为是在分组后需要找最新值字段

注意:

先查询出最新的结果存入临时表,再进行联表查询,数据量大一点的情况下 可以对创建完的临时表加索引,使整个过程的复杂度趋近f(n)。

4.新增字段标识

从业务层面优化,在表上加一个字段,isnewdata,bit型或者int型就好,每次在表中提交数据时,事务中先将要提交的数据所涉及的id,将历史数据isnewdata=1的更新为0,新提交的数据,isnewdata为1,然后增加一个索引isnewdata,或者如果需要和其他表关联的时候,增加复合索引,性能一下就上来了。

5.使用mysql用户变量提供排序序列号

示例:

SET @rank := 0;
SET @cgroup := NULL;
SELECT
a.*
IF
( @cgroup = a.分组字段, @rank := @rank + 1, @rank := 1 ) AS rank_no,
@cgroup := a.分组字段,
FROM
表 a
ORDER BY
a.分组字段,
a.排序字段 ASC

解释:

分组字段:进行分组的字段
排序字段:分组后排序的字段
@rank :序号计数变量
@cgroup :分组字段暂存变量,用来比较是否进入了下一组数据

运行逻辑:

该sql首先会进行排序,先分组字段,再排序字段,这点很关键,然后根据mysql的service层执行顺序对展示数据进行处理,先进入if函数判断数据是累加还是初始化@rank用户变量(注意首先第一次进来@cgroup为空),然后将分组字段的值赋予@cgroup变量。整个执行过程非常精巧有意思,得细细品味。

注意:

不支持开窗函数的mysql版本可以使用该方式,但是要ORM支持多行SQL代码块才行,还要修改数据库的SQL_MODE,mybatis和JPA都可以,Oracle不行 需要配置。比如mycat这种数据库中间件

附:MySQL 取出每个分组中最新的一条数据(ID最大)

场景:由于一个摄像头管理一个范围,且管理的某个人可以多次犯规。故,一个摄像头可以上报有多个事件,多个事件可能同时上报,可能有先后顺序。

需求:现地图只显示有事件摄像头的最新一条事件信息,故,需要ID倒序后,以摄像头分组,以此获得摄像头最新的一条事件信息。(这里以ID倒序而不以创建时间倒序,是有一定优势的)。

思路:先ID(时间)倒序,后分组。

说明:  如果是单表查询,默认分组后是ID最大的一条数据,若是连表查询,需要以下方法处理。

SELECT t.id,t.camera_id,t.create_time FROM 
    (SELECT e.* FROM sys_enforce_event e LEFT JOIN sys_dict_node n ON e.node_id = n.id 
    WHERE e.is_deleted = 0 AND e.source_id = 2 AND n.`code` IN (200,301,302,400,500) AND e.camera_id IS NOT NULL ORDER BY e.id DESC ) t    
GROUP BY t.camera_id 

以下方式就不对了,查找的不是最新一条记录(连表查询,看清区别)

SELECT e.* (SELECT * FROM sys_enforce_event ORDER BY id DESC ) e LEFT JOIN sys_dict_node n ON e.node_id = n.id
    WHERE e.is_deleted = 0 AND e.source_id = 2 AND n.`code` IN (200,301,302,400,500 ) AND e.camera_id IS NOT NULL     
GROUP BY e.camera_id 

注意事项:此方式仅支持低于5.7版本的MySql(SELECT VERSION();)

关于高于5.7版本的,可在排序后添加l imit 999999,即可为最新一条。

总结:

如果业务数据量不大,最多就几万条,用方案1,2,3均可。数据量不超过100万行,方案1和3还能勉强顶住。超过100万行,就要从业务层面去优化了,此时选择方案4是明智的

到此这篇关于Mysql分组查询取最新的几种方案总结的文章就介绍到这了,更多相关Mysql分组查询取最新内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文