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

相关推荐
wafrdggh8 小时前
2026电商快递批量查询怎么用?固乔助手模板设置评测
大数据·数据库·小程序
大大大大晴天10 小时前
OLTP、OLAP、数据湖、数据集市、湖仓一体到底是什么关系?
大数据
正在走向自律13 小时前
AI Agent 落地这一年:从“能跑起来”到“敢放出去”
大数据·人工智能·ai agent·智能体·mcp·a2a
freejackman14 小时前
【实战二】字段级血缘追踪:拆解 AST 迷局,精准穿透函数链与子查询
大数据·数据库·sql
goujunwe14 小时前
GEO 内容的“可摘录性”打磨技巧:段落、表格、小标题怎么写,方便大模型提取独立答案块
大数据·人工智能
小田学Python15 小时前
DeepSeek Harness 完整上手指南:安装部署 + 必装插件 + 用一周就回不去的功能
大数据·大模型·agent·ai工具·deepseek
成都蜀味缘餐饮技术培训学校18 小时前
手撕烤兔技术培训实战指南,秘制腌料配方比例与调制手法
大数据·经验分享·笔记·其他·微信公众平台
roman_日积跬步-终至千里19 小时前
AI 能让一个人成为团队吗?从数据中台到 OPC,重新理解软件工程能力
大数据·人工智能·软件工程
演艺大赏20 小时前
NiFi 数据流实战:FTP→HDFS 与 CSV→Parquet,附坏数据、漏文件与双写
大数据
数字新视界21 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统