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

2023-05-16

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 BY
	t1.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 BY
	t1.customer_id;

在这里插入图片描述

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

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

SELECT
	*
FROM
	customer_wallet_detail 
WHERE
	id IN ( SELECT MAX( id ) FROM customer_wallet_detail GROUP BY customer_id ) 
ORDER BY
	customer_id;

在这里插入图片描述

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

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

SELECT
	t1.* 
FROM
	customer_wallet_detail t1
	INNER 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还是排序的第一条
    在这里插入图片描述

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • hashCode和equals作用

    什么是hashCode hashCode 的作用是获取哈希码 xff0c 返回一个int整数 xff0c 作用是查找hashMap的索引位置 hashCode 在JDK的Object类中 xff0c 也就表明每一个类中都有此方法 hashC
  • python学习——while True的用法

    古月金真 while True 是python中经常会被应用到 下面通过个例子进行解释 xff1a 例子 xff1a 建立一个用户登录系统 xff0c 用户输入用户名和密码 xff0c 如果正确就可以进入系统 法一 xff1a d 61 古
  • linux下将gbk文件转换为utf-8

    titledatetagscategories linux下将gbk文件转换为utf 8 2019 11 23 10 49 58 0800 编码 problems 转自Linux下GBK文件编码批量转换UTF 8命令 在工作中 xff0c
  • ubuntu上不了网的解决方法(简便)

    用于我们对于ubuntu的使用比较多 xff0c 网络有时需要更改设置 下面笔者分享一个实用的方法 1 增加多一个网络连接 将鼠标移至左上角上下箭头 xff0c 之后点击爱编辑连接 xff08 不同版本操作方法不一样但其实都是一样的操作路径
  • C++ 快读快输模板

    C 43 43 快读快输模板 快读 span class token keyword inline span span class token keyword int span span class token function read
  • sql server2008支持json函数以及2016版内置json函数

    一 sql server2008支持json函数 1 json 转化成数据集合 1 xff09 转化用函数 CREATE FUNCTION dbo parseJSON 64 JSON NVARCHAR MAX RETURNS 64 hier
  • 家庭宽带 动态公网IP,使用腾讯云DDNS实现实时更新DNS解析记录

    解决DDNS问题 动态公网IP 环境说明 xff1a 我是家庭宽带 只能申请到动态的公网ip xff0c 好处是不花钱 xff0c 弊端是每次重启光猫 xff0c 都会重新获取一个新的公网IP 为解决此办法 xff0c 我尝试了很多个DDN
  • 用c语言实现一个简单的计算器(数据结构)

    概要 xff1a 主要以c语言为例对数据结构中写一个简易的计算器 xff08 计算整数间加减乘除括号运算 xff09 的算法进行大致讲述 xff0c 细说如何去实现符号优先级的比较的函数实现 注 xff1a 由于编辑器缘故 xff0c 本文
  • apache网页中文乱码解决方法

    apache的配置文件 xff1a 进入httpd conf xff0c 在文件的最后添加AddDefaultCharset gb2312 xff0c 注意 xff0c 新版本的apache是没有默认的AddDefaultCharset字段
  • 将ubuntu设置为NAS——搭建Jellyfin影音服务器

    Jellyfin安装 前提是你已经安装好了docker docker ps 查看docker 安装容器 docker ps a 下载docker jellyfin的镜像文件 sudo docker pull jellyfin jellyfi
  • CommonJS概述及使用

    在web开发中 xff0c 我们的js文件都是在html文件中引入 xff0c 在浏览器环境运行 但是在nodejs的环境中可没有html文件 xff0c 且nodejs中 xff0c 有且只有一个入口文件index js xff08 在执
  • HDFS(一)HDFS基本介绍

    HDFS基本介绍 HDFS的Master Slave结构HDFS角色作用简介HDFS 分块存储抽象成数据块的好处HDFS 副本机制名字空间 xff08 NameSpace xff09 NameNode 功能DataNode 功能机架感知原理
  • C++借助宏来实现由枚举值得到枚举变量名的字符串

    定义一个枚举 enum color span class token punctuation span RED span class token operator 61 span span class token number 0 span
  • 阿里云配置域名CDN加速

    前置条件 1 xff1a 阿里云账号 2 xff1a 已备案的域名 3 xff1a 拥有公网IP的服务器 登录控制台 新增源站 xff08 也就是你的云服务器IP xff09 按提示配置完成后进入到CDN管理页面 复制CNAME列的加速域名
  • Mybatis-PageHelper自定义count查询

    1 问题描述 使用分页插件pagehelper实现分页功能 xff0c 有个接口查询速度慢 2 问题原因 排查问题发现是因为在分页时 xff0c 查询总条数的sql执行慢 xff0c 而查询总条数的sql是在分页插件执行时 xff0c 在主
  • Python Django API项目构建

    项目构建心得 Python Django项目构建心得一 基础1 1 虚拟环境1 1 1 安装1 1 2 创建虚拟环境1 1 3 激活 退出 虚拟环境 1 2 Django命令 二 项目构建2 1 创建项目2 2 构建分类配置文件2 3 安装
  • 华为服务器Linux系统配置

    华为服务器Linux系统配置 前言 此文档是为了初次使用华为H22M 03 CPU SSD服务器 Ubuntu系统安装与配置 xff08 注 xff1a 合适的显示屏还是比较重要的 xff0c 分辨率越高越优先 xff09 的用户所提供的一
  • Share 很喜欢的派大星图片

    今天给大家分享一波我很喜欢的派大星头像 早起的派大星 奔跑的派大星 卖萌的派大星 可爱的派大星 冬日里的派大星 行走的派大星 二货派大星 老板范儿派大星 呆楞的派大星 无奈的派大星嫌弃的派大星 努力工作的派大星 666的派大星 这是我们安全
  • redis+lua脚本实现秒杀扣减库存 & SpringBoot环境+Redisson客户端

    redis 43 lua脚本实现秒杀扣减库存 amp SpringBoot环境 43 Redisson客户端 前言为什么使用Redisson项目搭建maven配置编写Redisson配置类编写Application yml编写启动类编写测试
  • Mysql分组查询每组最新的一条数据(三种实现方法)

    MySQL分组查询每组最新的一条数据 前言注意事项准备SQL错误查询错误原因 方法一方法二 xff08 适用于自增ID和创建时间排序一致 xff09 方法三 xff08 适用于自增ID和创建时间排序一致 xff09 总结MAX 函数和MIN