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

这篇具有很好参考价值的文章主要介绍了分组排序取第一条数据 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 表结构

字段名 字段类型 说明
id Bigint Id
addtime Timestamp 添加时间
userid Bigint 用户 id
adminid Bigint 管理员 Id
ask Longtext 留言
reply Longtext 回复
isreply Int 是否回复

3.3 表数据概览

Id Addtime Userid Adminid Ask Reply Isreply
1 2024-04-15 13:05:00 1 1 提问1 0
142 2024-04-15 13:04:00 2 1 提问2 0
143 2024-04-15 13:03:00 3 1 提问3 0
144 2024-04-15 13:01:00 4 1 提问4 0
182 2024-04-22 14:31:27 1713703513670 1 123 0
183 2024-04-22 14:31:33 1713703513670 1 12312321 0
184 2024-04-22 14:31:36 1713703513670 1 1232133 0

3.4 查询语句

查询的思路就是根据用户 Id 分组,按添加时间倒序(从大到小)排,取每个用户 Id 分组中的第一条数据。为了方便查看,在查询最后再将记录根据添加时间倒序排列,将留言时间最近的用户放在前面。文章来源地址https://www.toymoban.com/news/detail-857571.html

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 rownum
    FROM
		(SELECT @x:=- 1) t0, 
			(SELECT * FROM chat 
				ORDER BY userid , addtime DESC) t) t2
	WHERE t2.rownum = 1 order by addtime desc; 

3.5 结果

Addtime Userid Ask
2024-04-22 14:31:36 1713703513670 1232133
2024-04-15 13:05:00 1 提问 1
2024-04-15 13:04:00 2 提问 2
2024-04-15 13:03:00 3 提问 3
2024-04-15 13:01:00 4 提问 4

到了这里,关于分组排序取第一条数据 SQL写法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mysql分组排序取每组第一条(二种实现方式)

    记录一下最近的一个需求,查不同产品排名第一的图片作为主图 其实一开始想用的是 mybatis的级联查询 ,结果说需要一次性全部查出来 那就没事了,改 sql 咯: 亲测实用 MySQL:8.0 Java:1.8 建表语句: 这一种也是网上推荐最多的,但个人觉得局限性太大,不介意用在实战上

    2024年02月06日
    浏览(29)
  • Clickhouse学习系列——一条SQL完成gourp by分组与不分组数值计算

           笔者在近一两年接触了Clickhouse数据库,在项目中也进行了一些实践,但一直都没有一些技术文章的沉淀,近期打算做个系列,通过一些具体的场景将Clickhouse的用法进行沉淀和分享,供大家参考。       首先我们假设一个Clickhouse数据表: 这个表格的字段含义如注释,

    2024年02月14日
    浏览(28)
  • SQL基础语法 | 增删改查、分组、排序、limit

    创建数据库 查看数据库 选择数据库 删除数据库 创建表格 删除表格 创建schema模式 删除schema模式 删除一个空模式 删除一个模式及包含的所有对象 1. 增insert into sql语法 : INSERT INTO TABLE_NAME (字段名1, column2, column3,...columnN) VALUES (value1, value2, value3,...valueN) 2. 查询select SQL语法:

    2024年02月15日
    浏览(34)
  • Spark SQL自定义collect_list分组排序

    想要在spark sql中对group by + concat_ws()的字段进行排序,可以参考如下方法。 原始数据如下: 目标数据如下: spark-shell: 1.使用开窗函数 因为使用开窗函数本身会使用比较多的资源, 这种方式在大数据量下性能会比较慢,所以尝试下面的操作。 2.使用struct和sort_array(array,asc?tru

    2024年02月01日
    浏览(30)
  • SQL Server删除重复数据只保留一条

    介绍 最近在导入数据库数据, 有时候给的数据源文件,存在重复数据, 需要清除但是还需要保留一条记录的需求. 本文将介绍如何使用SQL Server来实现这个需求。 流程 下面是实现删除重复数据的流程,我们可以用表格展示每个步骤: 步骤    描述 步骤一    先看看有哪些重复

    2024年02月01日
    浏览(33)
  • SQL | 分组数据

    两个新的select子句:group by子句和having子句。 10.1-数据分组 上面我们学到了,使用SQL中的聚集函数可以汇总数据,这样,我们就能够对行进行计数,计算和,计算平均数。 目前为止,所有的计算都是在表的所有数据或者匹配特定的where子句的数据上进行的。 如上述SQL语句,返

    2024年02月13日
    浏览(21)
  • 数据库编程大赛:一条SQL计算扑克牌24点

    你是否在寻找一个平台,能让你展示你的SQL技能,与同行们一较高下?你是否渴望在实战中提升你的SQL水平,开阔你的技术视野?如果你对这些都感兴趣,那么本次由NineData主办的《数据库编程大赛》,将是你的最佳选择! 本次数据库编程大赛的奖项安排:一等奖(1人)、二

    2024年02月03日
    浏览(34)
  • sql Left Join 关联多条数据情况下只取一条数据

    存在b表中foreign_key多条关联a表中的id 需要只取b表中的一条数据(例如取最新的时间的一条) 1.解决方式:使用ROW_NUMBER () over()新增一列编号,排序后对新增列进行筛选 2.解决方式:使用listagg函数多列转一行,再结合substr函数截取最后一条

    2024年02月06日
    浏览(32)
  • mysql根据一个表的数据更新另一个表数据的SQL写法

    概述 :用一个表中的字段去更新另外一个表中的字段, MySQL 中有相应的 update 语句来支持,不过这个 update 语法有些特殊。看一个例子就明白了。 方法一: 结果: 结论: 代码对应的城市更新,对应错误的更正; city表中没有的城市,在people表里全被更新为null。 方法二: 结果

    2024年02月09日
    浏览(31)
  • SQL实现一行数据分组后转多行多列

    在统计一些指标时,通常会有多个指标需要分组进行聚合,但是 数据源的粒度可能并非是指标分组的粒度 。举个例子,比如从访客表中提取访客的数据,每行数据有每个平台的首次访问时间;另外要做一个平台统计表,其中的一个指标统计的是各个平台近1天、7天、30天的新

    2024年02月14日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包