函数() OVER(
PARTITION BY col -- 分区(可选)
ORDER BY col -- 排序(部分函数必需)
ROWS|RANGE BETWEEN ... -- 帧(仅聚合/首尾类有效)
)
类别 | 函数 | 帧有效 | 需要 ORDER BY |
|---|---|---|---|
排名 | ROW_NUMBER, RANK, DENSE_RANK | ✗ | ✓ |
偏移 | LAG, LEAD | ✗ | ✓ |
聚合 | SUM, AVG, COUNT, MAX, MIN | ✓ | 通常需要 |
首尾定位 | FIRST_VALUE, LAST_VALUE, NTH_VALUE | ✓ | ✓ |
分布 | NTILE, PERCENT_RANK, CUME_DIST | ✗ | ✓ |
函数 | 行为 | 重复值处理 | 典型场景 |
|---|---|---|---|
ROW_NUMBER() | 连续编号 1,2,3,4,5 | 不并列,不跳号 | 去重取最新、分组 Top-N |
RANK() | 并列跳号 1,1,3,3,5 | 并列同号,后续跳号 | 排名允许并列 |
DENSE_RANK() | 并列不跳号 1,1,2,2,3 | 并列同号,后续不跳 | 连续等级划分 |
-- 每部门薪资 Top 3(允许并列)
SELECT * FROM (
SELECT *, DENSE_RANK() OVER(PARTITION BY dept ORDER BY salary DESC) as rk
FROM employee
) t WHERE rk <= 3;
函数 | 语法 | 说明 |
|---|---|---|
LAG(col, n, default) | 取前第 n 行 | 默认 n=1,default=NULL |
LEAD(col, n, default) | 取后第 n 行 | 默认 n=1,default=NULL |
-- 环比计算
SELECT
month, amount,
amount - LAG(amount, 1) OVER(ORDER BY month) as mom_change
FROM sales;
-- 计算相邻事件间隔
SELECT
user_id, event_time,
DATEDIFF(event_time, LAG(event_time) OVER(PARTITION BY user_id ORDER BY event_time)) as gap_days
FROM user_log;
函数 | 说明 | 常见帧搭配 |
|---|---|---|
SUM() | 求和 | 累计求和、滑动求和 |
AVG() | 平均 | 移动平均 |
COUNT() | 计数 | 滑动窗口内行数 |
MAX() / MIN() | 极值 | 滑动最大/最小值 |
-- 累计求和(默认帧)
SUM(amount) OVER(PARTITION BY user_id ORDER BY month)
-- 近3行移动平均
AVG(amount) OVER(PARTITION BY user_id ORDER BY month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
-- 全分区求和(不加 ORDER BY 则帧为整个分区)
SUM(amount) OVER(PARTITION BY dept)
函数 | 说明 | 注意事项 |
|---|---|---|
FIRST_VALUE(col) | 帧内第一行的值 | — |
LAST_VALUE(col) | 帧内最后一行的值 | ⚠️ 默认帧陷阱 |
NTH_VALUE(col, n) | 帧内第 n 行的值 | n 从 1 开始 |
-- LAST_VALUE 陷阱:默认帧是 UNBOUNDED PRECEDING TO CURRENT ROW
-- 必须显式指定帧才能拿到分区最后一行
LAST_VALUE(amount) OVER(PARTITION BY name ORDER BY month
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
函数 | 公式 | 结果范围 | 说明 |
|---|---|---|---|
NTILE(n) | — | 1 ~ n | 等分为 n 桶 |
PERCENT_RANK() | (rank - 1) / (rows - 1) | 0 ~ 1 | 百分比排名 |
CUME_DIST() | count(≤当前值) / rows | 0 ~ 1 | 累积分布比例 |
-- 按销售额分4档
NTILE(4) OVER(ORDER BY amount DESC) as quartile
-- 百分位排名
PERCENT_RANK() OVER(PARTITION BY dept ORDER BY salary) as pct
帧定义并非对所有窗口函数生效。只有计算结果依赖"看哪些行"的函数才受帧影响:
类别 | 帧有效 | 原因 |
|---|---|---|
聚合类(SUM/AVG/COUNT/MAX/MIN) | ✓ | 帧决定对哪些行做聚合运算 |
首尾定位类(FIRST_VALUE/LAST_VALUE/NTH_VALUE) | ✓ | 帧决定在哪个范围内取第几行 |
排名类(ROW_NUMBER/RANK/DENSE_RANK) | ✗ | 排名基于整个分区的相对位置,不存在"部分排名" |
偏移类(LAG/LEAD) | ✗ | 语义固定为"前/后第n行",与帧范围无关 |
分布类(NTILE/PERCENT_RANK/CUME_DIST) | ✗ | 分布计算需要完整分区数据 |
实际行为:对帧无效的函数,即使写了
ROWS BETWEEN ...,数据库会直接忽略,不会报错,但也不会生效。
{ROWS | RANGE} BETWEEN <起点> AND <终点>
分区第一行模式 | 写法 | 用途 |
|---|---|---|
累计 | ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW | 累计求和(默认) |
滑动窗口 | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW | 近3行移动平均 |
居中窗口 | ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING | 平滑处理 |
整个分区 | ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING | 分区总计 |
ROWS | RANGE | |
|---|---|---|
计算方式 | 按物理行数 | 按排序列的值 |
重复值 | 每行独立计算 | 相同值的行视为同一位置 |
性能 | 更快 | 略慢 |
默认 | — | 有 ORDER BY 时的默认帧类型 |
-- 示例:ORDER BY amount,数据为 900, 900, 1100
-- ROWS: 第1行累计=900, 第2行累计=1800
-- RANGE: 第1行累计=1800, 第2行累计=1800(两个900一起纳入)
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW⚠️ 这是
LAST_VALUE陷阱的根源:有 ORDER BY 时默认帧只到当前行,所以 LAST_VALUE 返回的是当前行自己。
ROW_NUMBER() OVER(PARTITION BY ... ORDER BY ... DESC)
+ WHERE rk <= N