mysql8之前如何实现row_number() over(partition by xxx order by xxx asc/desc)

news2024/12/22 23:35:06

文章目录

  • 背景
  • 问题
  • 分析
  • 难点
  • 解决方案:
  • 总结公式
    • 多字段作为分组如何处理

背景

最近笔者在进行对广告业务的数据统计时遇到这种情况,业务方嫌弃离线数仓太慢,又无需太高的实时性本该使用即席查询的OLAP去做,但是当前公司调研的OLAP还没有推到广告业务侧,无奈只得使用mysql暂时顶一下。我们当前使用的是mysql5.7。

一充用户:当日只有一次充值的用户,二充三充一次类推
笔单价:用户充值金额 / 用户充值笔数

问题

本次遇到了一个广告业务监控的指标,统计在一充、二充、三充用户最后一笔充值的笔单价

分析

我们发现一二三充这种维度并不难做,分析发现要想实现用户最后一笔订单笔单价的统计,首先需要实现分组下用户最后一笔订单金额和充值笔数的统计,这就需要把订单按照用户分组交易时间倒排取出用户的最后一次充值,这时候我们想到了开窗函数。

难点

但是mysql在mysql8之后才支持开窗函数,mysql5.7本身是没有开窗函数的,而做这个需求实现一个和hive一样开窗函数的功能。

解决方案:

笔者在调研了之后发现可以使用定义常量的方式进行实现。首先我们看如何实现分组排序,首先想要实现

row_number() over(partition by user_id order by trade_time desc)

这样的功能,我们需要

1. 首先定义好分组变量(@user_id)和顺序变量(@rank),这一步非常重要,否则会造成第二次执行会在第一次排名的基础继续增加。坑笔者已踩,希望大家避免不要暴雷。
2. 对数据先按user_id排序,正序倒序都可以,目的是使得同一个用户的数据聚集在一起,模拟开窗函数的partition by的分组聚集的功能,但是这里仅仅是聚集数据而非分组数据。数据在变量对比中实现的分组
3. 然后在按照trade_time降序排序,模拟开窗函数order by的排序功能
4. 这个时候我们就需要利用变量进行函数累计实现类似的一个排名操作,实际上它只是数据的行号

@rank :=CASE WHEN @user_id=o.user_id THEN @rank + 1 ELSE 1 END 

逻辑中我们判断当@user_id等于user_id时,这个时候是同一个用户的订单数据来了所以我们对这个用户的数据的行号进行+1,因为数据已经在前面做过排序所以行号+1 正是数据在分组中的顺序。那么问题来了第一条数据或者用户的第一条数据来的时候变量中明显不是当前的user_id,第一条数据来的时候user_id是null,用户第一条数据来的时候他是另一个用户id或者null。我们分析第一条数据那他的顺序就是1,所以我们将这种情况rank else为1指定排名。然后将排名更新到顺序变量中,等待下次累加。
于是我们写出了如下代码:

set @user_id=null;
set @rank=0;
select 
   o.user_id AS user_id,
   o.id,
   o.amount,
   (@rank :=CASE WHEN @user_id=o.user_id THEN @rank + 1 ELSE 1 END ) as row_number
from (
        select
           o.user_id AS user_id,
           o.id,
           o.amount
        from (
           select 1 as user_id,1001 as id, 90 AS amount, '2023-05-19 00:01:11' as trade_time union all 
           select 1 as user_id,1002 as id, 67 AS amount, '2023-05-19 03:01:38' as trade_time union all
           select 1 as user_id,1003 as id, 88 AS amount, '2023-05-19 02:01:21' as trade_time union all
           select 2 as user_id,1004 as id, 78 AS amount, '2023-05-19 01:01:12' as trade_time union all
           select 2 as user_id,1005 as id, 66 AS amount, '2023-05-19 02:01:47' as trade_time union all
           select 2 as user_id,1006 as id, 55 AS amount, '2023-05-19 05:01:19' as trade_time union all
           select 3 as user_id,1007 as id, 99 AS amount, '2023-05-19 05:01:51' as trade_time union all
           select 3 as user_id,1008 as id, 94 AS amount, '2023-05-19 04:01:30' as trade_time union all
           select 3 as user_id,1009 as id, 90 AS amount, '2023-05-19 07:01:57' as trade_time 
        ) o
        order by o.user_id,o.trade_time desc
   ) o;
  1. 不要以为这就完了,我们测试现在我们代码
    在这里插入图片描述
    发现现在的结果并不是我们想要的,我们分析一下,我们光判断了@user_id=user_id,那么我们什么时候向里面写我们的值了呢?,突然发现原来万丈高楼连个地基都没有是空中楼阁啊!所以我们(铁律1:)在字段中必须有字段给常量赋值的逻辑 ( @user_id := o.user_id ),否则不能实现分组排序;好了,现在要想java一样给变量赋值了,那好我们进行赋值操作:
set @user_id=null;
set @rank=0;
select 
   @user_id:=o.user_id AS user_id,
   o.id,
   o.amount,
   (@rank :=CASE WHEN @user_id=o.user_id THEN @rank + 1 ELSE 1 END ) as row_number
from (
        select
           o.user_id AS user_id,
           o.id,
           o.amount
        from (
           select 1 as user_id,1001 as id, 90 AS amount, '2023-05-19 00:01:11' as trade_time union all 
           select 1 as user_id,1002 as id, 67 AS amount, '2023-05-19 03:01:38' as trade_time union all
           select 1 as user_id,1003 as id, 88 AS amount, '2023-05-19 02:01:21' as trade_time union all
           select 2 as user_id,1004 as id, 78 AS amount, '2023-05-19 01:01:12' as trade_time union all
           select 2 as user_id,1005 as id, 66 AS amount, '2023-05-19 02:01:47' as trade_time union all
           select 2 as user_id,1006 as id, 55 AS amount, '2023-05-19 05:01:19' as trade_time union all
           select 3 as user_id,1007 as id, 99 AS amount, '2023-05-19 05:01:51' as trade_time union all
           select 3 as user_id,1008 as id, 94 AS amount, '2023-05-19 04:01:30' as trade_time union all
           select 3 as user_id,1009 as id, 90 AS amount, '2023-05-19 07:01:57' as trade_time 
        ) o
        order by o.user_id,o.trade_time desc
   ) o;

我们执行代码结果如下:
在这里插入图片描述
wtf ?什么鬼不是说好了给我赋值我就能行了吗?我们接着分析我的代码逻辑是如何判断的我判断第一个变量来的时候@user_id直接被赋值为实际user_id,判断相等这时候为1了,可是我们想我们都在判断前赋值了他还有机会为不相等的时候吗?显然没有了,这就出现了我们常量中的值因为一直相等,一直+1,最后成了全局排序。那我们于是乎把赋值移到判断后边。测试代码如下:
在这里插入图片描述
我们发现这一次的结果正是我们想要的,于是我们总结下:
铁律2:分组赋值字段必须在排序字段后面,否则是针对所有数据排序。最终排序代码如下:

set @user_id=null;
set @rank=0;
select 
   o.id,
   o.amount,
   o.trade_time,
   (@rank :=CASE WHEN @user_id=o.user_id THEN @rank + 1 ELSE 1 END ) as row_number,
   @user_id:=o.user_id AS user_id
from (
        select
           o.user_id AS user_id,
           o.id,
           o.amount,
           o.trade_time
        from (
           select 1 as user_id,1001 as id, 90 AS amount, '2023-05-19 00:01:11' as trade_time union all 
           select 1 as user_id,1002 as id, 67 AS amount, '2023-05-19 03:01:38' as trade_time union all
           select 1 as user_id,1003 as id, 88 AS amount, '2023-05-19 02:01:21' as trade_time union all
           select 2 as user_id,1004 as id, 78 AS amount, '2023-05-19 01:01:12' as trade_time union all
           select 2 as user_id,1005 as id, 66 AS amount, '2023-05-19 02:01:47' as trade_time union all
           select 2 as user_id,1006 as id, 55 AS amount, '2023-05-19 05:01:19' as trade_time union all
           select 3 as user_id,1007 as id, 99 AS amount, '2023-05-19 05:01:51' as trade_time union all
           select 3 as user_id,1008 as id, 94 AS amount, '2023-05-19 04:01:30' as trade_time union all
           select 3 as user_id,1009 as id, 90 AS amount, '2023-05-19 07:01:57' as trade_time 
        ) o
        order by o.user_id,o.trade_time desc
   ) o;

到这里分组排序就已经给大家说完了。

总结公式

学学其他大佬我们也来总结总结公式
要想实现

row_number() over(partition by 分组字段 order by 排序字段 desc)

的功能我们进行如下替换:

set @分组字段=null;
set @顺序字段=0;
select 
   o.cloumn1,
   o.cloumn2,
   o.cloumn3,
   (@rank :=CASE WHEN @分组字段=o.分组字段 THEN @rank + 1 ELSE 1 END ) as row_number,
   @user_id:=o.分组字段 AS 分组字段
from (
        select
           o.分组字段,
            [o.cloumn1,
            o.cloumn2,
            o.cloumn3,
            排序字段]
        from table o
        order by o.分组字段,o.排序字段 desc
   ) o;

那么到这里有同学就来提问了我要多字段分组怎么办?我们一次发挥一下:

多字段作为分组如何处理

我们依照公式直接在相关地方加上分组字段看看行不行?我们在数据中加上我想看一充二充三充用户在不同广告id下的最后一笔客单价,我们在数据中加上ad_id。按照以上公式逻辑得出代码:

set @user_id=null;
set @ad_id=null;
set @rank=0;
select 
   o.id,
   o.amount,
   o.trade_time,
   (@rank :=CASE WHEN @user_id=o.user_id and @ad_id=o.ad_id THEN @rank + 1 ELSE 1 END ) as row_number,
   @user_id:=o.user_id AS user_id,
   @ad_id:=o.ad_id AS ad_id
from (
        select
           o.user_id,
           o.ad_id,
           o.id,
           o.amount,
           o.trade_time
        from (
           select 1 as user_id,1001 as id, 1111 AS ad_id, 90 AS amount, '2023-05-19 00:01:11' as trade_time union all 
           select 1 as user_id,1002 as id, 1112 AS ad_id, 67 AS amount, '2023-05-19 03:01:38' as trade_time union all
           select 1 as user_id,1003 as id, 1111 AS ad_id, 88 AS amount, '2023-05-19 02:01:21' as trade_time union all
           select 2 as user_id,1004 as id, 1112 AS ad_id, 78 AS amount, '2023-05-19 01:01:12' as trade_time union all
           select 2 as user_id,1005 as id, 1112 AS ad_id, 66 AS amount, '2023-05-19 02:01:47' as trade_time union all
           select 2 as user_id,1006 as id, 1112 AS ad_id, 55 AS amount, '2023-05-19 05:01:19' as trade_time union all
           select 3 as user_id,1007 as id, 1114 AS ad_id, 99 AS amount, '2023-05-19 05:01:51' as trade_time union all
           select 3 as user_id,1008 as id, 1114 AS ad_id, 94 AS amount, '2023-05-19 04:01:30' as trade_time union all
           select 3 as user_id,1009 as id, 1113 AS ad_id, 90 AS amount, '2023-05-19 07:01:57' as trade_time 
        ) o
        order by o.ad_id,o.user_id,o.trade_time desc
   ) o;

测试结果如下:
在这里插入图片描述
果然好用,到此结束本篇,希望可以帮助到大家。

注意:多分组维度覆盖范围大的维度放在前面

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

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

相关文章

Unity 2022 版本 寻路 NavMesh

首先装包 先给地图 和 阻挡 设置为静态 然后给地上行走的地方 添加组件 可以直接bake 然后会显示蓝色的可行走路径 player 添加插件 然后给角色添加脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlayerMove : Mon…

SpringBoot自动配置底层源码解析

1,配置分类 对于一个Spring项目,主要就是有两种配置 一种是类似端口号、数据库地址、用户名密码等一种是各种Bean,比如整合Mybatis需要配置的MapperFactoryBean,比如整合事务需要配置DataSourceTransactionManager SpringBoot中…

Github copilot几个使用技巧,自动补全代码

一、常用快捷键 快捷键含义tab应用提示代码esc拒绝提示代码ctrlenter打开提示面板选用10个意见代码中的一个Alt]切换建议代码Alt ->逐个应用代码 这些快捷键其实就是红色框的功能,也可以通过鼠标点击操作 下面具体介绍一下常用的三个功能: 1. tab自…

Maven——Maven仓库

1.概念 2.远程仓库 3.本地仓库 4.仓库配置和JDK配置 配置远程仓库&#xff1a; <mirror><id>alimaven</id><mirrorOf>central</mirrorOf><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/publ…

EAI(Enterprise Application Integration,企业应用集成)

目录 1.表示集成&#xff08;界面集成&#xff0c;iframe&#xff09; 2.数据集成&#xff08;中间件&#xff0c;数据库上面一层&#xff09; 3.控制集成&#xff08;API&#xff09; 4.业务流程集成 5.企业之间的应用集成 ​最后推荐一个图片转文字网站 最后推荐一个抖音去…

PowerShell if 使用参考

if 参考 与许多其他语言一样&#xff0c;PowerShell 提供了用于在脚本中有条件地执行代码的语句。 其中一个语句是 If 语句。 今天&#xff0c;我们将深入探讨 PowerShell 中最基本的命令之一。 Powershell 下载&#xff0c;参考 PowershellPowershell 相关文档&#xff0c;…

常用在线工具,非常实用,快收藏起来!

作者丨黑蛋 今天给大家介绍一些常用到的在线工具&#xff0c;能方便我们的日常学习&#xff1a; 编码工具&#xff1a; AES加密解密&#xff1a;http://www.jsons.cn/aesencrypt/ DNA编码解码&#xff1a;https://web.expasy.org/translate/ 双16进制编码解码&#xff1a;ht…

素材发布资源下载 OSS存储+用户组打折+下载限速 V1.1.3(one_market)

插件简介 插件用于各类 资源下载站、数字产品下载站、作品模型下载网站、数字市场网站 等 所有插件都使用管理控制台进行管理,方便后续的统一管理。 管理操作使用AJAX交互,站长管理更加高效快捷。让站长体验更加方便、快捷、高效的管理操作 插件管理功能 全局设置 [基本设置]…

[AI图片生成]自己搭建StableDiffusion安装过程

前言 最近尝试玩玩AI图片生成,安装一路坑 出个一路安装成功的记录 开始 找个空间大的盘符,这玩意将来会很占空间.一个模型大约5g左右,你可能还会装很多模型创建个目录,路径不要有中文安装git 下载地址 详细教程 (如果有忽略)下载 Python3.10.0,记得勾选添加到环境变量选项,安…

看板管理解析:如何通过看板提升项目管理效率?

在目前市面上的项目管理工具中&#xff0c;项目看板功能基本上成为了标配。看板作为敏捷的项目管理工具&#xff0c;可以帮助我们将项目工作可视化展现。 项目看板的作用 1&#xff0c;提高团队信息流动性&#xff1a;看板工具可以及时的传递项目工作中的最新讯息&#xff0c;保…

CC++动态内存管理

C&C动态内存管理 C语言动态内存管理 关于C语言动态内存管理实际上就三个函数malloc和calloc以及realloc&#xff0c;更多的是去理解&#xff0c;用C语言去实现数据结构阶段如果细心你就会可以发现&#xff0c;所有的数据结构都是使用动态内存管理的方式&#xff0c;在堆区…

什么是自然语言处理的文本分析?

自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;是一种人工智能技术&#xff0c;旨在使计算机能够理解、解释和生成自然语言。文本分析是NLP的一个重要领域&#xff0c;它涉及到从文本数据中提取有用信息的过程。本文将详细介绍自然语言处理的…

如何压缩pdf文件的大小?四种方法值得收藏

如何压缩pdf文件的大小&#xff1f;实际上&#xff0c;压缩PDF文件的主要原因是为了减小文件的大小以便于存储、传输和分享。通常情况下&#xff0c;PDF文件包含大量的图像、文本和其他媒体元素&#xff0c;因此它们的文件大小可能会非常大。如果您需要通过电子邮件或网络共享P…

Qt- QSS样式表用法及用例说明

这里写自定义目录标题 QSS样式表用法1.Qt样式表语法2.选择器3.属性列表4.冲突解决5.全局添加QSS QSS样式表用法 整理qss样式表语法知识&#xff0c;方便今后查看。 1.Qt样式表语法 Qt样式表支持各种属性、伪状态和子控件&#xff0c;可以自定义小部件的外观 selector { attr…

这篇文章教你截图怎么翻译

在我们日常生活和工作中&#xff0c;可能会遇到一些需要翻译的文字内容&#xff0c;例如外语文件、国外的路标等。此时&#xff0c;我们也可以选择手动输入这些文字进行翻译&#xff0c;但是这样不仅费时费力&#xff0c;还容易出现翻译错误的情况。相比之下&#xff0c;我认为…

基于三维数字地球的智慧水利防洪数字沙盘解决方案,助力水利工程数字化升级

简介&#xff1a; 水利防洪电子沙盘主要是基于三维 GIS 平台&#xff0c;采用遥感&#xff08;RS&#xff09;、地理信息系统&#xff08;GIS&#xff09;、虚拟现实&#xff08;VR&#xff09;等技术&#xff0c;在三维电子沙盘场景建设的基础上&#xff0c;加入基础地理信息…

腾讯天幕:荣获首届“IPv6技术应用创新大赛”全国总决赛优秀奖

近日&#xff0c;首届“IPv6技术应用创新大赛”全国总决赛圆满落下帷幕。经过层层选拔、激烈角逐&#xff0c;腾讯参赛项目“IPv6时代下的腾讯天幕安全算力算法PaaS”凭借旁路部署、高阻断率、海量流量实时监控及处理、大数据实时处理分析、联动开放等核心优势&#xff0c;从15…

Java面试知识点(全)- Java并发-多线程JUC二-原子类/锁

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 JUC原子类 什么是CAS CAS的全称为Compare-And-Swap&#xff0c;直译就是对比交换。是一条CPU的原子指令&#xff0c;其作用是让CPU先进行比较两个值…

人体传感器SR501控制继电器

人体传感器SR501 原理 红外热释电检测移动人体 缺点 只能识别移动人体&#xff08;静止的不行&#xff09; 容易误判 正面 背面电路 跳线 H&#xff1a;触发周期可重复触发&#xff0c;一般选用此 L&#xff1a;不可重复触发&#xff0c;关掉之后才会重新触发 封锁周期 …

windows目录共享

开启SMB 1.0/CIFS服务器 打开控制面板 将 “SMB 1.0/CIFS文件共享支持” 这个勾上&#xff0c;点击确定。 选中一个要共享的文件夹&#xff0c;右键“属性”-->“共享”-->“高级共享” 勾上“共享次文件夹”&#xff0c;点击“权限” “组或用户名”选择“Everyone”,权…