MaxCompute中窗口函数

文章目录


一、分类总览

1、函数分类

分类 主要解决的问题 代表函数
1、聚合统计类 在窗口内求和、均值、最大值、最小值、中位数、标准差 SUM、AVG、COUNT、MAX、MIN、MEDIAN、STDDEV、STDDEV_SAMP
2、排名 / 分组类 对分区内数据编号、排名、分桶 ROW_NUMBER、RANK、DENSE_RANK、NTILE
3、前后取值类 获取第一条、最后一条、第 N 条、前后 N 行 FIRST_VALUE、LAST_VALUE、NTH_VALUE、LAG、LEAD
4、分布 / 百分位类 计算累计分布、百分比排名、百分位 CUME_DIST、PERCENT_RANK、PERCENTILE_CONT、PERCENTILE_DISC
5、随机抽样类 在窗口 / 分区内随机抽样 CLUSTER_SAMPLE

2、全部函数

分类 函数 功能
1、聚合统计类 AVG 计算窗口内平均值
1、聚合统计类 COUNT 计算窗口内记录数
1、聚合统计类 MAX 计算窗口内最大值
1、聚合统计类 MEDIAN 计算窗口内中位数
1、聚合统计类 MIN 计算窗口内最小值
1、聚合统计类 STDDEV 计算窗口内总体标准差
1、聚合统计类 STDDEV_SAMP 计算窗口内样本标准差
1、聚合统计类 SUM 计算窗口内总和
2、排名 / 分组类 DENSE_RANK 连续排名,相同值同名次且不跳号
2、排名 / 分组类 NTILE 将有序数据切成 N 份并返回分组编号
2、排名 / 分组类 RANK 排名,相同值同名次但可能跳号
2、排名 / 分组类 ROW_NUMBER 为分区内每行生成唯一行号
3、前后取值类 FIRST_VALUE 获取当前窗口第一条数据的值
3、前后取值类 LAG 获取当前行之前第 N 行的值
3、前后取值类 LAST_VALUE 获取当前窗口最后一条数据的值
3、前后取值类 LEAD 获取当前行之后第 N 行的值
3、前后取值类 NTH_VALUE 获取当前窗口第 N 条数据的值
4、分布 / 百分位类 CUME_DIST 计算累计分布
4、分布 / 百分位类 PERCENT_RANK 计算百分比排名
4、分布 / 百分位类 PERCENTILE_CONT 计算连续型精确百分位,可插值
4、分布 / 百分位类 PERCENTILE_DISC 计算离散型百分位,返回实际值
5、随机抽样类 CLUSTER_SAMPLE 在窗口内随机抽样,返回是否被抽中

二、窗口函数基础

关键字 功能
OVER 声明窗口函数
PARTITION BY 将数据划分为多个独立分区
ORDER BY 定义分区内的排序顺序
ROWS 按行数定义窗口范围
RANGE 按排序字段的值范围定义窗口
GROUPS 按相同排序值组成的组定义窗口
PRECEDING 当前行之前
FOLLOWING 当前行之后
CURRENT ROW 当前行
UNBOUNDED PRECEDING 分区起点
UNBOUNDED FOLLOWING 分区终点

1、定义

窗口函数会在当前行对应的"窗口"中进行计算,同时保留原始明细行。

基本结构:

sql 复制代码
函数(...) OVER (
    PARTITION BY 分组字段
    ORDER BY 排序字段
    ROWS BETWEEN 起点 AND 终点
)

2、区别

关键字 核心含义
PARTITION BY 决定"和谁一起算"
ORDER BY 决定"先后顺序"
ROWS 按具体行数确定范围
RANGE 按排序字段值的范围确定窗口
GROUPS 按相同排序值形成的组确定窗口

窗口函数与普通 GROUP BY 最大区别:

GROUP BY 会把多行聚合为更少的行;窗口函数保留原始行,只在每行后面增加计算结果。

官方使用限制:

  • 窗口函数只能出现在 SELECT 中。
  • 窗口函数内部不能嵌套窗口函数和聚合函数。
  • 窗口函数不能与同级别聚合函数直接混用。
  • FILTER 只适用于聚合类窗口函数,例如 COUNT、SUM、AVG、MAX、MIN 等。

3、简单记忆

PARTITION BY = 分组。

ORDER BY = 排序。

ROWS = 按行。

RANGE = 按值范围。

GROUPS = 按同值组。

OVER = 在这个窗口上计算。

4、案例

sql 复制代码
-- 结果:每个部门内部按工资升序计算累计工资
SELECT deptno, ename, sal, SUM(sal) OVER(PARTITION BY deptno ORDER BY sal ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_sal FROM emp;

-- 结果:只让 value > 100 的记录参与窗口 SUM 计算
SELECT key, value, SUM(value) FILTER(WHERE value > 100) OVER(PARTITION BY key ORDER BY value) AS total_value FROM mf_window_fun;

三、聚合统计类

函数 功能
SUM 窗口求和
AVG 窗口求平均值
COUNT 窗口计数
MAX 窗口最大值
MIN 窗口最小值
MEDIAN 窗口中位数
STDDEV 窗口总体标准差
STDDEV_SAMP 窗口样本标准差

1、定义

这一类与普通聚合函数的计算逻辑基本一致,但增加 OVER(...) 后不会压缩明细行。

2、区别

函数 核心区别
SUM 总和
AVG 平均值
COUNT 数量
MAX 最大值
MIN 最小值
MEDIAN 中位数
STDDEV 总体标准差
STDDEV_SAMP 样本标准差

最容易混淆:

对比 区别
普通 SUM vs 窗口 SUM 前者通常减少行数;后者保留明细
AVG vs MEDIAN 平均值 vs 中位数
STDDEV vs STDDEV_SAMP 总体标准差 vs 样本标准差

3、简单记忆

SUM 加起来。

AVG 平均。

COUNT 数数量。

MAX / MIN 最大 / 最小。

MEDIAN 找中间。

STDDEV 总体标准差。

STDDEV_SAMP 样本标准差。

4、案例

sql 复制代码
-- 结果:每行显示所在部门的工资总额
SELECT deptno, ename, sal, SUM(sal) OVER(PARTITION BY deptno) AS dept_sum FROM emp;

-- 结果:每行显示所在部门的平均工资
SELECT deptno, ename, sal, AVG(sal) OVER(PARTITION BY deptno) AS dept_avg FROM emp;

-- 结果:每行显示所在部门的员工数量
SELECT deptno, ename, COUNT(*) OVER(PARTITION BY deptno) AS dept_cnt FROM emp;

-- 结果:每行显示所在部门的最高工资
SELECT deptno, ename, sal, MAX(sal) OVER(PARTITION BY deptno) AS dept_max FROM emp;

-- 结果:每行显示所在部门的最低工资
SELECT deptno, ename, sal, MIN(sal) OVER(PARTITION BY deptno) AS dept_min FROM emp;

-- 结果:每行显示所在部门的工资中位数
SELECT deptno, ename, sal, MEDIAN(sal) OVER(PARTITION BY deptno) AS dept_median FROM emp;

-- 结果:每行显示所在部门工资的总体标准差
SELECT deptno, ename, sal, STDDEV(sal) OVER(PARTITION BY deptno) AS dept_stddev FROM emp;

-- 结果:每行显示所在部门工资的样本标准差
SELECT deptno, ename, sal, STDDEV_SAMP(sal) OVER(PARTITION BY deptno) AS dept_stddev_samp FROM emp;

四、排名 / 分组类

函数 功能
ROW_NUMBER 唯一连续编号
RANK 并列排名,之后跳号
DENSE_RANK 并列排名,之后不跳号
NTILE 按顺序切成 N 组

1、定义

这一类都依赖 ORDER BY 的排序结果,对分区内数据进行编号、排名或分桶。

2、区别

假设排序结果为:

text 复制代码
100
100
90
80
值 ROW_NUMBER RANK DENSE_RANK
100 1 1 1
100 2 1 1
90 3 3 2
80 4 4 3
函数 相同值处理 是否跳号
ROW_NUMBER 不并列 不存在并列
RANK 并列 跳号
DENSE_RANK 并列 不跳号
NTILE 不排名 切成 N 组

3、简单记忆

ROW_NUMBER = 每行唯一编号。

RANK = 并列后跳号。

DENSE_RANK = 排名很密,不跳号。

NTILE = 切成 N 份。

4、案例

sql 复制代码
-- 结果:部门内工资从高到低生成唯一序号 1、2、3...
SELECT deptno, ename, sal, ROW_NUMBER() OVER(PARTITION BY deptno ORDER BY sal DESC) AS rn FROM emp;

-- 结果:相同工资并列,后续可能为 1、1、3
SELECT deptno, ename, sal, RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) AS rk FROM emp;

-- 结果:相同工资并列,后续为 1、1、2
SELECT deptno, ename, sal, DENSE_RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) AS dense_rk FROM emp;

-- 结果:部门内按工资从高到低切成 3 组
SELECT deptno, ename, sal, NTILE(3) OVER(PARTITION BY deptno ORDER BY sal DESC) AS salary_group FROM emp;

五、前后取值类

函数 功能
FIRST_VALUE 当前窗口第一条值
LAST_VALUE 当前窗口最后一条值
NTH_VALUE 当前窗口第 N 条值
LAG 当前行之前第 N 行
LEAD 当前行之后第 N 行

1、定义

这一类根据窗口排序,从其他行获取对应字段值。

2、区别

函数 参照对象
FIRST_VALUE 当前窗口第一行
LAST_VALUE 当前窗口最后一行
NTH_VALUE 当前窗口第 N 行
LAG 相对当前行往前
LEAD 相对当前行往后

核心区别:

FIRST_VALUE / LAST_VALUE / NTH_VALUE 是看"窗口里的固定位置";

LAG / LEAD 是看"当前行前后偏移"。

3、简单记忆

FIRST_VALUE 第一条。

LAST_VALUE 最后一条。

NTH_VALUE 第 N 条。

LAG 往前看。

LEAD 往后看。

4、案例

sql 复制代码
-- 结果:返回部门内按工资排序后的第一条工资
SELECT deptno, ename, sal, FIRST_VALUE(sal) OVER(PARTITION BY deptno ORDER BY sal) AS first_sal FROM emp;

-- 结果:返回部门内按工资排序后的最后一条工资
SELECT deptno, ename, sal, LAST_VALUE(sal) OVER(PARTITION BY deptno ORDER BY sal ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_sal FROM emp;

-- 结果:返回部门窗口中的第 2 条工资
SELECT deptno, ename, sal, NTH_VALUE(sal, 2) OVER(PARTITION BY deptno ORDER BY sal ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_sal FROM emp;

-- 结果:返回当前员工前一行工资,第一行返回 NULL
SELECT deptno, ename, sal, LAG(sal, 1) OVER(PARTITION BY deptno ORDER BY sal) AS prev_sal FROM emp;

-- 结果:返回当前员工后一行工资,最后一行返回 NULL
SELECT deptno, ename, sal, LEAD(sal, 1) OVER(PARTITION BY deptno ORDER BY sal) AS next_sal FROM emp;

六、分布 / 百分位类

函数 功能
CUME_DIST 累计分布
PERCENT_RANK 百分比排名
PERCENTILE_CONT 连续型精确百分位
PERCENTILE_DISC 离散型百分位

1、定义

这一类用于分析一条数据在整体分布中的位置,或计算 P50、P90、P95 等百分位。

2、区别

函数 核心含义
CUME_DIST 当前值及之前累计占整体的比例
PERCENT_RANK 当前排名在整体中的百分比位置
PERCENTILE_CONT 连续型百分位,可通过插值产生不存在于原数据中的值
PERCENTILE_DISC 离散型百分位,结果一定来自实际数据

例如数据:

text 复制代码
1, 2, 3, 4

P50:

函数 结果特点
PERCENTILE_CONT 可以得到 2.5
PERCENTILE_DISC 返回实际存在的数据值

3、简单记忆

CUME_DIST = 累计占比。

PERCENT_RANK = 排名百分比。

CONT = Continuous,可以插值。

DISC = Discrete,只拿实际值。

4、案例

sql 复制代码
-- 结果:返回每名员工工资的累计分布比例
SELECT deptno, ename, sal, CUME_DIST() OVER(PARTITION BY deptno ORDER BY sal) AS cume_dist FROM emp;

-- 结果:返回每名员工工资的百分比排名
SELECT deptno, ename, sal, PERCENT_RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) AS pct_rank FROM emp;

-- 结果:返回部门工资精确 P50,可能产生插值
SELECT deptno, ename, sal, PERCENTILE_CONT(sal, 0.5) OVER(PARTITION BY deptno) AS p50 FROM emp;

-- 结果:返回部门工资离散 P50,结果来自实际工资值
SELECT deptno, ename, sal, PERCENTILE_DISC(sal, 0.5) OVER(PARTITION BY deptno) AS p50 FROM emp;

七、随机抽样类

函数 功能
CLUSTER_SAMPLE 在窗口 / 分区内随机抽样

1、定义

CLUSTER_SAMPLE 用于对窗口内数据进行随机抽样,返回 BOOLEAN:

  • TRUE:当前行被抽中。
  • FALSE:当前行未被抽中。

2、区别

它与排名后取前 N 条的最大区别:

对比 区别
ROW_NUMBER 按明确排序规则确定性取数
CLUSTER_SAMPLE 随机抽样

3、简单记忆

CLUSTER_SAMPLE = 分区内随机抽样。

4、案例

sql 复制代码
-- 结果:每个部门随机抽样,被抽中的行返回 TRUE
SELECT deptno, ename, CLUSTER_SAMPLE(1) OVER(PARTITION BY deptno) AS is_sampled FROM emp;

八、最容易混淆的函数对比

函数组合 最核心区别
ROW_NUMBER vs RANK 唯一编号 vs 相同值并列排名
RANK vs DENSE_RANK 并列后跳号 vs 并列后不跳号
ROW_NUMBER vs NTILE 每行编号 vs 将数据切成 N 组
LAG vs LEAD 往前取值 vs 往后取值
FIRST_VALUE vs LAG 窗口第一条 vs 当前行前 N 条
LAST_VALUE vs LEAD 窗口最后一条 vs 当前行后 N 条
NTH_VALUE vs LAG / LEAD 窗口固定位置 vs 相对当前行偏移
SUM OVER vs GROUP BY SUM 保留明细行 vs 聚合减少行数
AVG vs MEDIAN 平均值 vs 中位数
STDDEV vs STDDEV_SAMP 总体标准差 vs 样本标准差
CUME_DIST vs PERCENT_RANK 累计分布比例 vs 排名百分比
PERCENTILE_CONT vs PERCENTILE_DISC 可插值 vs 只返回实际值
ROWS vs RANGE 按行数范围 vs 按排序字段值范围
RANGE vs GROUPS 按值范围 vs 按相同排序值组成的组
ROW_NUMBER vs CLUSTER_SAMPLE 确定性排序编号 vs 随机抽样

九、常用案例

1、分组取最新一条

sql 复制代码
-- 结果:每个 shop_id 只保留 etl_time 最新的一条
SELECT *
FROM (
    SELECT t.*, ROW_NUMBER() OVER(PARTITION BY shop_id ORDER BY etl_time DESC) AS rn
    FROM source_table t
) a
WHERE rn = 1;

2、按多个字段去重

sql 复制代码
-- 结果:按 start_date、end_date、city 去重,每组保留 ds 最新的一条
SELECT *
FROM (
    SELECT t.*, ROW_NUMBER() OVER(PARTITION BY TRIM(start_date), TRIM(end_date), TRIM(city) ORDER BY ds DESC) AS rn
    FROM source_table t
) a
WHERE rn = 1;

3、累计求和

sql 复制代码
-- 结果:每个门店按日期计算累计销售额
SELECT shop_id, biz_date, amount, SUM(amount) OVER(PARTITION BY shop_id ORDER BY biz_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS total_amount FROM sales;

4、计算环比

sql 复制代码
-- 结果:获取前一天金额,并计算与当前金额差值
SELECT shop_id, biz_date, amount, LAG(amount, 1) OVER(PARTITION BY shop_id ORDER BY biz_date) AS prev_amount, amount - LAG(amount, 1) OVER(PARTITION BY shop_id ORDER BY biz_date) AS diff_amount FROM sales;

5、获取下一条记录

sql 复制代码
-- 结果:获取同一门店的下一条业务日期
SELECT shop_id, biz_date, LEAD(biz_date, 1) OVER(PARTITION BY shop_id ORDER BY biz_date) AS next_date FROM sales;

6、分组 TOP N

sql 复制代码
-- 结果:每个城市销售额最高的前 3 个门店
SELECT *
FROM (
    SELECT city, shop_id, amount, ROW_NUMBER() OVER(PARTITION BY city ORDER BY amount DESC) AS rn
    FROM shop_sales
) t
WHERE rn <= 3;

十、参考资料

阿里云 MaxCompute 官方文档:

https://help.aliyun.com/zh/maxcompute/window-functions-1

相关推荐
2601_967212721 小时前
国际品质电源轨道系统供应商技术评估维度
大数据·网络·经验分享
智商网输送线配件2 小时前
流水线全套配件直供化落地的三个实操切入维度
大数据·运维·自动化·智商网·流水线设备配件
字节跳动数据平台2 小时前
几小时的视频找 3 秒镜头,你还在一格格拖进度条?
大数据
IT毕设梦工厂2 小时前
计算机毕业设计选题推荐:基于大数据的城市噪音数据分析与可视化|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·信息可视化·数据分析·spark·毕业设计·课程设计·数据可视化
IT古董2 小时前
《FDE前沿部署工程师实战教程》23 - Enterprise AI Event Bus:事件驱动、消息队列与AI工作流自动触发
大数据·数据库·人工智能
阿尔法工场研究院2 小时前
宇树们不想重蹈新造车的覆辙
大数据·人工智能·科技·机器人
中电金信2 小时前
中电金信参编的团体标准《商业银行应用程序接口治理能力要求》正式发布
大数据·运维·人工智能
SelectDB3 小时前
1TB/天 × 30 天日志成本怎么估:核对命令、降冷配置与踩坑记录
大数据·数据库·数据分析
SelectDB3 小时前
日志选型别只比 Loki 和 ELK:三条路径、一份可复制的落地清单
大数据·数据库·数据分析