分组排序取第一条数据 SQL写法

1. 背景

在数据库查询过程中经常遇到需要分组排序查询第一条数据的情况。例如,在消息列表中需要展示每个联系人最近的一条信息。

2. 解决方案

目前我接触到的解决方案有两种,分别是开窗函数 row_number 和变量法。

2.1 开窗函数法

比较常用的解决方案是使用开窗函数 row_number() over(partition by xxx order by xxx) 。使用开窗函数比较简便,只需要两个步骤

2.1.1 分组+排序+标注序号

select *, row_number()  over (partition by ${group_col} order by ${order_col} ) as rownum 
from ${table}

以上 SQL 中 ${group_col} 表示要分组的字段;${order_col} 表示排序字段;rownum 是为序号字段取的别名。这条 SQL 的含义是将数据表 table 中的数据根据 group_col 分组后根据 order_col 进行排序,并为每条数据标注出在当前分组中的序号。

2.1.2 取第一条

select * from t1 where rownum = 1
  • t1分组+排序+标注序号 后的表,这里为了简洁就直接用 t1 代表子查询了。
  • rownum分组+排序+标注序号 中序号的别名。where rownum = 1 表示取 分组+排序+标注序号 后的第一条数据。

SQL 的含义是从 分组+排序+标注序号 后的表中查询序号为 1 的数据,这里的序号是各分组中排序后的序号。

3.1.3 Demo

将上述两个步骤连贯起来的 Demo 如下。

select * from (select *, row_number() over (partition by ${group_col} order by ${order_col} )  rownum from ${table}) t1 
where rownum = 1 

2.2 变量法

开窗函数的方法在 Hive 和高版本的 MySQL 里都是比较简单的查询方法,但是在低版本的 MySQL (比如 MySQL 5.7)中不支持开窗函数。所以只能换另一种方法,就是变量法。变量法需要通过变量来达成开窗函数的效果所以比较复杂,主要分为三部:数据排序、添加序号、取第一条。

2.2.1 数据排序

select * from ${table} order by ${group_col}, ${order_col}

首先对 table 表中的数据进行排序,排序的字段中第一个必须是想要用于分组的字段 group_col,后面才是需要排序的字段 order_col

2.2.2 添加序号

SELECT t.*, IF(@x = ${group_col} OR (@x IS NULL AND ${group_col} IS NULL), @rank:=@rank + 1, @rank:=1 AND @x:= ${group_col}) as rownum from (SELECT @x:=- 1) t0, t

这条 SQL 中定义了两个变量 xrank,整个 SQL 都围绕这两个变量展开,下面分别解释一下相关的内容。

  • x 变量用于保存当前分组的字段值,比如当前分组的字段值为 1,如果后面发现分组字段值不等于 1 了则说明换到另一个分组了,需要重新计数。
  • rank 变量用于保存当前分组中上一条记录的序号,如果上一条记录的序号是 1,则当前记录序号为 2。
  • (SELECT @x:=- 1) 的作用是为 x 变量初始化一个默认值,如果不初始化默认值则 x 默认为 nullx 的默认值必须是分组字段中没有的值,不建议设置为 null
  • IF(@x = ${group_col} OR (@x IS NULL AND ${group_col} IS NULL), @rank:=@rank + 1, @rank:=1 AND @x:=${group_col}) as rownum 这条语句就是序号赋值的关键所在。含义为如果 x 与当前记录的分组字段值相同则 rank = rank + 1(同分组内序号递增),否则 rank=1 (不同分组重新开始计数)并且 @x:=${group_col}(将 x 赋值为当前记录的分组字段值)。
  • t数据排序 后的表,这里为了简洁用 t 来代替子查询。

2.2.3 取第一条

select * from t2 where t2.rownum = 1

t2添加序号 后的表,这里为了简洁也是用 t2 代替子查询。

2.2.4 Demo

将上述三个步骤融合在一起如下。

select * from (SELECT t.*, IF(@x = ${group_col} OR (@x IS NULL AND ${group_col} IS NULL), 		@rank:=@rank + 1, @rank:=1 AND @x:= ${group_col}) as rownum from (SELECT @x:=- 1) as t0, (select * from ${table} order by ${group_col}, ${order_col}) as t) as t2 where t2.rownum = 1

3. 例子

3.1 需求

有一张留言记录表,表中记录的是每个用户的留言和管理员的回复。现需要取每个用户最新的一条留言记录

3.2 表结构

字段名字段类型说明
idBigintId
addtimeTimestamp添加时间
useridBigint用户 id
adminidBigint管理员 Id
askLongtext留言
replyLongtext回复
isreplyInt是否回复

3.3 表数据概览

IdAddtimeUseridAdminidAskReplyIsreply
12024-04-15 13:05:0011提问10
1422024-04-15 13:04:0021提问20
1432024-04-15 13:03:0031提问30
1442024-04-15 13:01:0041提问40
1822024-04-22 14:31:27171370351367011230
1832024-04-22 14:31:3317137035136701123123210
1842024-04-22 14:31:361713703513670112321330

3.4 查询语句

查询的思路就是根据用户 Id 分组,按添加时间倒序(从大到小)排,取每个用户 Id 分组中的第一条数据。为了方便查看,在查询最后再将记录根据添加时间倒序排列,将留言时间最近的用户放在前面。

3.4.1 开窗函数法

select * from (select *, row_number()  over (partition by userid order by addtime desc)  rownum from chat where ask is not null ) t1 where rownum = 1 order by addtime desc

3.4.2 变量法

select addtime, userid, ask from ((select *, IF(@x = userid OR (@x IS NULL AND userid IS NULL), @rank:=@rank + 1, @rank:=1 AND @x:=userid) AS rownumFROM(SELECT @x:=- 1) t0, (SELECT * FROM chat ORDER BY userid , addtime DESC) t) t2WHERE t2.rownum = 1 order by addtime desc; 

3.5 结果

AddtimeUseridAsk
2024-04-22 14:31:3617137035136701232133
2024-04-15 13:05:001提问 1
2024-04-15 13:04:002提问 2
2024-04-15 13:03:003提问 3
2024-04-15 13:01:004提问 4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/2942.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

杰发科技AC7840——CAN通信简介(6)_监听模式

参考:http://t.csdnimg.cn/AFFPC 0. 简介 7840支持4种扩展模式,其中监听模式。 监听模式概念 作用: 这里写的用于诊断,实际上我还没有用到,不太理解为啥可以用作诊断。 我的理解是,在多个总线下,使用监听…

BUUCTF-MISC-10.LSB1

10.LSB1 题目:lsb隐写,stegsolve可以看到包含了一个PNG图片 使用stegsolve打开这个图片 由PNG文件头可以看出隐写内容为PNG文件,按save Bin键保存为PNG文件。 得到一张二维码图片,使用CQR扫一下

重启Nginx

1. 重启nginx命令: systemctl restart nginx service nginx restart ./nginx -s reload 2. 重启完查看进程: ps -ef | grep nginx

PostgreSQL中的临时表与永久表的区别,以及它们的最佳使用场景?

文章目录 临时表与永久表的区别临时表永久表区别总结 最佳使用场景临时表的使用场景永久表的使用场景 解决方案及示例代码临时表示例创建临时表插入数据查询数据 永久表示例创建永久表插入数据查询数据 总结 在PostgreSQL中,临时表和永久表都是用于存储数据的表结构…

Tensorflow小技巧01:检测本地Tensorflow的版本

前言: 以Pycharm为例,Windwos10系统,检测本地环境的Tensorflow的版本: 1 打开Pycharm窗口 2 在窗口中输入: pythonPython 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win…

智慧文旅:引领旅游产业智慧升级的创新模式

一、智慧文旅是什么? 智慧文旅是指以当地特色文化为核心,借助现代科技手段,实现旅游景区全面智慧升级的旅游模式。在智慧文旅中,新一代信息网络技术和装备得到充分运用,文化旅游基础设施得到新建和改善,特…

python安装包使用国内镜像源

文章目录 1、国内镜像源2、命令3、pip加速安装4、其他命令参考文献 提示:以下是本篇文章正文内容,下面案例可供参考 1、国内镜像源 清华大学 :https://pypi.tuna.tsinghua.edu.cn/simple/ 阿里云:http://mirrors.aliyun.com/pyp…

【唯美情侣爱情表白纪念HTML单页】

唯美情侣爱情表白纪念HTML单页 效果图部分代码领取代码下期更新预报 效果图 整图 背景图 部分代码 index.html <!DOCTYPE html> <html lang"en"><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"…

Java 基础:设计模式之工厂方法模式

工厂方法模式&#xff08;Factory Method Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一个创建对象的通用接口&#xff0c;但将实际创建逻辑推迟到子类中实现。这种模式允许客户端使用抽象接口来创建特定类型的对象&#xff0c;而无需了解具体的实现细节。以…

valgrind,memcheck的使用

一&#xff0c;valgrind介绍 ​ valgrind是一个开源的&#xff0c;检测内存泄漏的工具&#xff0c;通常在linux下使用&#xff0c;除此之外&#xff0c;他还能检测内存管理错误&#xff0c;线程bug等错误。粗浅的来讲&#xff0c;valgrind由两部分构成&#xff0c;一部分用来模…

爬虫学习笔记-数美验证

测试网址&#xff1a;智能验证码体验_图片验证码_数美科技数美科技智能验证码在线体验&#xff0c;智能识别风险用户级别&#xff0c;自行切换智能验证码难度及类型&#xff0c;提供滑动、拼图、点选、数字、动态等多种智能验证码服务&#xff0c;精准拦截机器行为。https://ww…

R语言详解二

一&#xff0c;列表详解 创建一个列表 > myList<-list(id2,name"张三",age20) > myList $id [1] 2$name [1] "张三"$age [1] 20 获取第一个元素 > myList[[2]] [1] "张三" 获取第一个子列表 > myList[2] $name [1] "张…

20240309web前端_第四次作业_完成随机点名程序

要求 一、结合抽奖案例完成随机点名程序&#xff0c;要求如下: 1.点击点名按钮&#xff0c;名字界面随机显示&#xff0c;按钮文字由点名变为停止 2.再次点击点名按钮&#xff0c;显示当前被点名学生姓名&#xff0c;按钮文字由停止变为点名 3.样式请参考css及html自由发挥完成…

解读宁波IATF16949认证:开启成功之门的钥匙️

&#x1f449;解读宁波IATF16949认证&#xff1a;&#x1f970;开启成功之门的钥匙&#x1f5dd;️ &#x1f432;在风起云涌的&#x1f4fa;商业浪潮中&#xff0c;&#x1f6b6;每一个追求卓越的&#x1f685;企业都渴望找到一把&#x1f511;开启成功之门的钥匙。&#x1f3…

Vscode配置C/C++编程环境@配置C和CPP的运行和调试环境@配置过程的相关问题@中文文件名乱码@build和debug方案组合配置

文章目录 abstractgcc/g文档和用法常见用例 目录.vscode中的相关文件说明tasks.jsonlaunch.jsonc_cpp_properties.json IDE或编辑器配置vscode配置相关指令和快捷键默认task配置和取消默认 配置文件C/C共用一组tasks.json/launch.json文件?关于注释内容示例&#x1f47a;tasks…

linux安装MySQL8.0,密码修改权限配置等常规操作详解

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

面试二十一、红黑树

性质&#xff1a; 插入&#xff1a; 旋转&#xff1a;

公司网页制作需要多少钱

公司网页制作需要多少钱&#xff1f;这是一个非常常见的问题。答案取决于您需要的功能和设计。一些小型企业网站可能只需要一些基本的功能&#xff0c;花费可能低至几百美元&#xff0c;而一些大型企业网站可能需要高级功能和设计&#xff0c;可能需要几万美元。 以下是一些考虑…

阿里云盘小白羊版3.24.33113

网盘下载 里云盘小白羊版是一款在官方客户端基础上进行二次开发制作而成的第三方客户端&#xff0c;它拥有完善的云盘客户端功能&#xff0c;支持文件的列出、移动、重命名、在线预览、下载文件、创建点连接等等一系列功能&#xff0c;官方客户端所拥有的的功能它都有&#xf…

matlab 对数坐标画图,及在曲线上加竖直线

matlab 对数坐标画图 方法一&#xff1a;直接对x、y值取对数&#xff0c;然后画图 plot(log(x), log(y), m, LineWidth,1, Marker,.);% ,Color,#EDB120 方法二&#xff1a;将x、y轴刻度改为对数形式 plot(x, y, r, LineWidth,1, Marker,); ax gca();% 获取当前坐标句柄 ax…