数据位移玩出花,SQL窗口函数:LAG/LEAD/FIRST/LAST案例解析
wptr33 2025-05-26 19:24 52 浏览
前些天有粉丝留言,多些高级查询案例总结分享,也有粉丝私信问了查询的案例问题,这里就一起安排上
粉丝问题
埋点表,想按照某个事件,把发生这个事件的时间到下次这个事件发生的时间中间所有的事件分成一个组应该怎么做?
解决思路
1.先获取到相邻两个特定事件之间的时间段;
2.再把所有事件与上面的结果1关联,时间段内的进行统一标识特定事件的开始发生时间;
最终就能通过开始发生时间完成分组了
Mysql 8.0+的话可以直接窗口函数来解决
WITH specific_events AS (
SELECT
user_id,
event_time AS session_start,
LEAD(event_time)OVER (PARTITIONBY user_id ORDERBY event_time) AS next_session_start
FROM events WHERE event_name ='特定事件')
SELECT
e.user_id,
e.event_time,
e.event_name,
s.session_start AS session_id
FROM events e
LEFTJOIN specific_events s
ON e.user_id = s.user_id
AND e.event_time >= s.session_start
AND (e.event_time < s.next_session_start OR s.next_session_start ISNULL)
要是mysql5.7+的话,就是用变量来了
-- 初始化变量
SET @group_id = 0;
SET @prev_user = NULL;
-- 执行分组查询
SELECT
user_id, event_time, event_name, group_id
FROM (
SELECT user_id, event_time, event_name,
@group_id := IF(event_name = '特定事件', IF(user_id = @prev_user, @group_id + 1, 1), @group_id) AS group_id,
@prev_user := user_id
FROM events
ORDER BY user_id, event_time) tmp
WHERE group_id > 0;
-- 可选:排除首个分组前的事件
下面是LAG/LEAD/FIRST/LAST的介绍及案例
分类 | 函数示例 | 说明 |
前后值函数 | LAG(col, n, default) | 取当前行前第n行的值 |
LEAD(col, n, default) | 取当前行后第n行的值 | |
首尾值函数 | FIRST_VALUE(col) | 窗口第一行的值 |
LAST_VALUE(col) | 窗口最后一行的值 |
一、LAG(col, n, default)
核心价值:不用自连接!轻松获取历史数据
经典场景:计算环比增长、用户行为路径分析
案例:电商GMV周环比暴增分析
SELECT
统计周,
GMV,
LAG(GMV, 1, 0) OVER (ORDER BY 统计周) AS 上周GMV, -- 默认0避免NULL
ROUND((GMV - LAG(GMV) OVER (ORDER BY 统计周))/LAG(GMV) OVER (ORDER BY 统计周)*100,2) AS 环比增长率
FROM 周销售报表
避坑指南:
1 必加ORDER BY:无排序的LAG就像乱序时空穿越!
2 处理NULL值:用default参数给历史数据兜底
3 性能优化:相同OVER()子句可复用,减少重复计算
二、LEAD(col, n, default)
核心价值:提前获取未来数据,轻松计算差值
经典场景:用户下一步行为预测、库存消耗预警
案例:用户行为路径分析
SELECT
user_id,
event_time,
current_action,
LEAD(current_action, 1, '退出') OVER (PARTITION BY user_id ORDER BY event_time) AS 下一步动作
FROM 用户行为日志
WHERE event_date = '2023-06-18'
避坑指南:
1 分区陷阱:一定要用PARTITION BY隔离不同用户/业务
2 数据边界:末尾数据默认返回NULL,用default参数给明确状态
3 性能杀器:避免在十亿级日志表全量使用,先过滤时间分区!
三、FIRST_VALUE(col)
核心价值:快速定位窗口起点,计算累计值神器
经典场景:用户首单金额分析、累计销售额计算
案例:用户生命周期价值分析
SELECT
user_id,
order_date,
amount,
FIRST_VALUE(order_date) OVER (PARTITION BY user_id ORDER BY order_date) AS 首单日期,
SUM(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS 累计消费
FROM 订单表
避坑指南:
1 排序陷阱:不加ORDER BY会导致取值为窗口随机第一行!
2 窗口帧控制:默认RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
3 去重技巧:结合DISTINCT快速获取用户首单信息
四、LAST_VALUE(col)
核心价值:捕捉窗口终点数据,动态跟踪最新值
经典场景:库存最新状态跟踪、用户最近一次登录时间
案例:动态库存监控
SELECT
product_id,
stock_time,
current_stock,
LAST_VALUE(current_stock) OVER (
PARTITION BY product_id
ORDER BY stock_time
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -- 关键!
) AS 最新库存
FROM 库存流水表
避坑指南:
1 窗口帧必改:默认只看到当前行!必须扩展窗口范围
2 性能警报:UNBOUNDED FOLLOWING在大数据量时慎用
3 替代方案:用LEAD(0)或反向排序的FIRST_VALUE更高效
四大函数通用避坑指南:
1 索引优化:窗口函数依赖排序,提前在ORDER BY字段建索引
2 内存控制:一次处理10万+数据时,调大数据库的sort_buffer_size
3 版本陷阱:MySQL 8.0+才能用,MariaDB 10.2+语法略有不同
4 执行顺序:窗口函数在WHERE之后执行,不能作为过滤条件
高阶组合技:四大函数合体
案例:用户复购间隔分析
WITH 用户订单 AS (
SELECT
user_id,
order_date,
LAG(order_date) OVER (PARTITION BY user_id ORDER BY order_date) AS 上次购买日,
FIRST_VALUE(order_date) OVER (PARTITION BY user_id ORDER BY order_date DESC) AS 最近购买日
FROM 订单表
)
SELECT
user_id,
DATEDIFF(order_date, 上次购买日) AS 购买间隔,
DATEDIFF(CURRENT_DATE(), 最近购买日) AS 沉默天数
FROM 用户订单
粉丝福利
你在工作中还遇到过哪些数据处理难题? 评论区告诉我,后面专题解决!
相关推荐
- redis的八种使用场景
-
前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...
- 基于Redis的3种分布式ID生成策略
-
在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...
- 基于OpenWrt系统路由器的模式切换与网页设计
-
摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...
- 这篇文章教你看明白 nginx-ingress 控制器
-
主机nginx一般nginx做主机反向代理(网关)有以下配置...
- 如何用redis实现注册中心
-
一句话总结使用Redis实现注册中心:服务注册...
- 爱可可老师24小时热门分享(2020.5.10)
-
No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...
- Apportable:拯救程序员,IOS一秒变安卓
-
摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...
- JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透
-
以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...
- 3月26日更新 快速施法自动施法可独立设置
-
2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...
- Redis 是如何提供服务的
-
在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...
- lua _G、_VERSION使用
-
到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...
- China's top diplomat to chair third China-Pacific Island countries foreign ministers' meeting
-
BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...
- 移动工作交流工具Lua推出Insights数据分析产品
-
Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...
- Redis 7新武器:用Redis Stack实现向量搜索的极限压测
-
当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...
- Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求
-
重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)