MySQL分组查询每组最新的一条数据

  • 前言
  • 注意事项
  • 准备SQL
  • 错误查询
    • 错误原因
  • 方法一
  • 方法二(适用于自增ID和创建时间排序一致)
  • 方法三(适用于自增ID和创建时间排序一致)
  • 总结
    • MAX()函数和MIN()这一类函数和GROUP BY配合使用存在问题

前言

在写报表功能时遇到一个需要根据用户id分组查询最新一条钱包明细数据的需求,在写sql测试时遇到一个有趣的问题,开始使用子查询根据时间倒序+group by customer_id发现查询出来的数据一直都是最旧的一条,而不是我需要的最新一条数据我明明已经倒序排了,后来总结出了三种解决方案如下。

注意事项

  • 数据库版本 Mysql5.7+
  • 执行 GROUP BY 语句的时候出现 sql_mode=only_full_group_by 解决方法(这里是Mysql8的解决方案,Mysql5.7也差不多自行百度即可)
    • 1、执行 select @@sql_mode; 查看sql模式

      select @@sql_mode;
      

    • 2、将sql_mode中的only_full_group_by模式剔除 重新设置sql_mode值,如果是使用JDBC连接需要重启项目才能生效。

      set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
      set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
      

准备SQL

这里模拟一个sql

DROP TABLE IF EXISTS `customer_wallet_detail`;
CREATE TABLE `customer_wallet_detail`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`customer_id` bigint(20) NULL DEFAULT NULL COMMENT '用户ID',`happen_amount` varchar(15)  NULL DEFAULT '0' COMMENT '发生金额 带'-'号的代表扣款',`balance_amount` varchar(15) NULL DEFAULT '0' COMMENT '可用余额',`create_time` bigint(20) NULL DEFAULT NULL COMMENT '发生时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT = '用户钱包明细' ;INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (1, 1, '100', '100', 1670300656630);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (2, 1, '-10', '90', 1670300656640);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (3, 1, '5', '95', 1670300656650);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (4, 3, '998', '998', 1670300656660);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (5, 3, '-100', '898', 1670300656670);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (6, 3, '-98', '800', 1670300656680);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (7, 2, '666', '666', 1670300656690);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (8, 2, '-66', '600', 1670300656695);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (9, 2, '-600', '0', 1670300656699);

错误查询

SELECT*
FROM( SELECT * FROM customer_wallet_detail ORDER BY create_time DESC ) t1
GROUP BYt1.customer_id;

错误原因

在mysql5.7以及之后的版本,如果GROUP BY的子查询中包含ORDER BY,但是 GROUP BY 不与 LIMIT 配合使用,ORDER BY会被忽略掉,所以子查询在 GROUP BY 时排序不会生效,可能是因为子查询大多数是作为一个结果给主查询使用,所以子查询不需要排序。

方法一

鉴于以上的原因我们可以添加上 LIMIT 条件来实现功能。
PS:这个LIMIT的数量可以先自行 COUNT 出你要遍历的数据条数(这个数据条数是所有满足查询条件的数据合,我这里共9条数据)

SELECT*
FROM( SELECT * FROM customer_wallet_detail ORDER BY create_time DESC LIMIT 9 ) t1
GROUP BYt1.customer_id;

方法二(适用于自增ID和创建时间排序一致)

方法一需要先 COUNT 查询然后将查询结果设置到 LIMIT 条件中比较麻烦,这里还可以使用 MAX() 函数来实现该功能。
PS:因为我这里的业务数据是有序插入的,使用主键自增id和create_time结果是一样的而且使用id查询效率更高,如果没有唯一且有序的id可以替代create_time那么就用方案一,不能直接使用 SELECT id,MAX(create_time) 这种操作来获取最新一条数据id,原因在总结中有详细描述。

SELECT*
FROMcustomer_wallet_detail
WHEREid IN ( SELECT MAX( id ) FROM customer_wallet_detail GROUP BY customer_id )
ORDER BYcustomer_id;

方法三(适用于自增ID和创建时间排序一致)

方法三和方法二实现逻辑基本一致只是将IN查询替换成了连接查询,本地20w条数据测试 方法三比方法二性能提升50%,有兴趣的可以增大数据集测试后续性能变化。

SELECTt1.*
FROMcustomer_wallet_detail t1INNER JOIN ( SELECT MAX( id ) AS id FROM customer_wallet_detail GROUP BY customer_id ) t2 ON t1.id = t2.id

总结

结合我的业务经过测试,目前看来方案三是最合适的,sql简单性能适中,方案一比方案二性能更差而且实现麻烦,最终选择那个方案主要看业务而定。

MAX()函数和MIN()这一类函数和GROUP BY配合使用存在问题

MAX()函数和MIN()这一类函数和GROUP BY配合使用,GROUP BY拿到的数据永远都是这个分组排序最上面的一条,而MAX()函数和MIN()这一类函数会将这个分组中最大或最小的值取出来,这样会导致查询出来的数据对应不上。

  • 正确查询:
  • 错误查询:这里的确拿到每个分组最新创建时间了但是拿的数据id还是排序的第一条

Mysql分组查询每组最新的一条数据(三种实现方法)相关推荐

  1. sql分组查询每组最新的一条数据

    原文地址:https://www.cnblogs.com/java-spring/p/11498457.html 开发中经常会遇到,分组查询最新数据的问题,比如下面这张表(查询每个地址最新的一条记录) ...

  2. MySQL使用group by分组查询每组最新的一笔数据

    问题来源 今天遇到一个查询数据库数据的问题:要获取每个分组最新的一笔数据,并将每个分组最新的一笔数据重新组成一个新的列表 这种场景,当然是想到了分组查询,使用 group by,思路也很清晰: 将所有 ...

  3. sql分组查询每组最新一条数据

    在开发过程中,写sql语句时往往会遇到分类查询最新的一条数据或某一列最大数据,此时就需要用到连表查询和分组查询, 先分组查询出每组最大的id,再进行连表查询 SELECT id,title,u_id, ...

  4. Oracle分组中获取时间最新的一条数据

    解析:根据QYID分组,按照INDATE不为空倒叙排序,mtime取分组中排序在第一位的数据,即为时间最新的一条数据. SELECT * FROM (SELECTROW_NUMBER () OVER ...

  5. MySQL分组后取每一组第N条数据

    1.知识点 group_concat()函数:聚合函数 1.功能:将group by产生的同一个分组中的值连接起来,返回一个字符串结果. 2.语法:group_concat( [distinct] 要 ...

  6. mysql 分组去重只保留最新创建时间的数据

    使用子查询先排序,在正常查询就可以了 SELECT * FROM (SELECT * FROM crisps_mch_manage_operate_logORDER BY create_time DE ...

  7. mysql分组取出每组地一条数据_MYSQL实现分组排序并取组内第一条数据

    一.需要实现分组排序并且取组内状态优先级最高的数据 有一张这样的数据表, 需求是根据error_type分组然后取status最小的第一条数据 第一种写法: select t.* from ( sel ...

  8. mysql查找出每个用户最新的一条订单的5种解决思路

    mysql查找出每个用户最新的一条订单的5种解决思路 一.使用窗口函数 使用窗口函数 – 使用窗口函数:可以视为规范固定写法 row_number() over(partition by 需要分区的列 ...

  9. mysql 分组查询最新

    mysql分组查询最新 看到网上说到的方法和我写的都一样,也不知道有没有更好的方法,等到解答. SELECT id,group_id from (SELECT id,group_id from tab ...

最新文章

  1. mysql 中文截取_mysql 截取中文字符
  2. 基于机器学习的捡球机器人设计与实现(探索)第4篇——电机驱动板选择(2019-03-02)
  3. 分布式系统架构与云原生—阿里云《云原生架构白皮书》导读
  4. 错误:无法访问android.app.Activity 找不到android.app.Activity的类文件
  5. C和指针之指针数组和指向数组的指针
  6. MongoDB的查询语法和SQL的SELECT语法做对比
  7. FreeRTOS调度器挂起与解除
  8. docker镜像指定安装源_Docker快速安装以及换镜像源
  9. 12016.xilinx裸机开发
  10. 电商主题BANNER传统促销喜庆红色节日海报,C4D打造时尚场景
  11. Jquery 选择器 特殊字符 转义字符
  12. copy 回顾总结:Java中抽象类与抽象方法
  13. oracle10g debian,Debian5下oracle10g安装时DISPLAY的设置
  14. 【Verilog HDL】4选1数据选择器
  15. 中国住户收入调查(CHIP)数据及问卷(1988-2008年)
  16. java javah_Javah 常见错误记录
  17. String类的getBytes()方法
  18. jQueryEasyU校验邮箱、手机号等
  19. Stacked Hourglass笔记源码(一)网络结构
  20. 金融投资大数据(1)-马科维茨资产组合基于excel

热门文章

  1. 关于office2016 程序停止运行。。。的问题
  2. 神州战神电脑关闭触摸板
  3. 【2018NOIP普及组】T2:龙虎斗 试题解析
  4. 获得淘宝商品详情高级版api接口
  5. CSS真好玩——用纯CSS画一轮新月
  6. 那些年的,那些人的,那些事。
  7. 通读Docs - 《OPL1000 自学整理教程》
  8. 简体繁体转换功能php,php如何实现简体繁体转换-PHP问题
  9. 做销售如何有效地维护好与客户的关系
  10. 小程序 身份认证服务器,如何实现微信小程序与.net core应用服务端的无状态身份验证...