Mysql分组查询每组最新一条数据的三种实现方法
作者:kerwin_code 发布时间:2024-01-13 16:32:04
前言
在写报表功能时遇到一个需要根据用户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 `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (1, 1, '100', '100', 1670300656630);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (2, 1, '-10', '90', 1670300656640);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (3, 1, '5', '95', 1670300656650);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (4, 3, '998', '998', 1670300656660);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (5, 3, '-100', '898', 1670300656670);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (6, 3, '-98', '800', 1670300656680);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (7, 2, '666', '666', 1670300656690);
INSERT INTO `test`.`customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `happen_time`) VALUES (8, 2, '-66', '600', 1670300656695);
INSERT INTO `test`.`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 ) 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
来源:https://blog.csdn.net/weixin_44606481/article/details/128200727


猜你喜欢
- mysql连接超时和mysql连接错误在生产环境中,偶尔且不规律的出现mysql连接超时和创建连接出错的问题:15-09-2020 13:2
- 1. TinyMCE免费,开源,轻量的在线编辑器,基于 javascript,高度可定制,跨平台。2. FCKEditor免费,开源,用户量
- 虽然在win2003配置PHP有点非主流,但你还是要会怎么弄。你也可以将本文的虚拟机看成是服务器,宿主机看成是客户端。不像Linux系统,由
- 一般我是用<%@ include %>方式来包含这个文件,主要是这样能够被包含页面会跟包含页面在编译时被编译成一个文件,里面的变
- 有个简单的查看方法,打开记事本,如要查看“Chr("119") w”,可以按下Alt+119 (先按住Alt不放,然后输
- suspect_pages 表位于 msdb 数据库中,是在 SQL Server 2005 中引入的。用于维护有关可疑页的信息的 susp
- 源码:#!/usr/bin/env python# -*- coding:utf-8 -*- import turtleimport tim
- 使用Ajax技术网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载刷新整个页面,这使得程序能够更快地回应用户的操作,如下笔记将简单介
- 1、效果图2、安装npm install --save nprogress基本用法NProgress.start();NProgress.d
- 1、目的:在Python中实现只读取扩展名为xlsx的文件解决方法:使用os模块。解决思路:1、确定目录2、循环遍历每一个文件3、筛选符合条
- javascript是种脚本语言,浏览器下载到哪儿就会执行到哪儿,这种特性会为编程提供方便,但也容易使程序过于凌乱,支离破碎。 js从功能上
- 这段时间在处理SQL server 2000 SP4补丁打不上的问题上花了不少时间,回头想想应该总结一下:系统说明:dell1800服务器,
- 什么叫做SPA单页应用单页Web应用 (single page web application,SPA) ,就是只有一张
- 导语说到童年爱玩的电脑游戏,你会想到什么?最国民的莫过于金山打字通,接着是扫雷、红心大战,而红极一时的单机游戏当属《大富翁》。嘻嘻 打字游戏
- 前言通常在项目中,一般都需要一种编程语言来操作数据库,使用Python来操作数据库有着天然的优势,因为Python的字典和MongoDB的文
- 根据“廖雪峰”的教程进行python学习,计划每天抽出1-2个小时的时间进行充电。Python是著名的“龟叔”Guido van Rossu
- 通常我们会在一些javascript的书籍上看到,使用Javascript保留字作为标识符(变量名、函数名、循环标记等)时,会引起程序报错!
- 数据准备moduls.py# 构建表结构from django.db import models# 表app01_publishclass
- 一、MatplotlibMatplotlib是Python中众多数据可视化库的鼻祖,其设计风格与20世纪80年代设计的商业化程序语言MATL
- 本文介绍了使用XMlhttp技术来生成html页面,值得借鉴。相关函数:<% ’定义xmlhttp funct