在 Oracle 中使用开窗函数(如ROW_NUMBER()
、LAG()
、LEAD()
)解决连续问题时,PARTITION BY
和ORDER BY
子句是开窗函数的核心组成部分,它们的作用如下:
一、PARTITION BY
的作用:分组计算
- 功能:将结果集按照指定的列进行分区,开窗函数会在每个分区内独立计算。
- 类比:类似于
GROUP BY
,但不会合并行,而是保留原始数据的每一行。 - 在连续问题中的作用:
- 按分组处理数据:例如,在分析用户连续登录时,需要按
USER_ID
分区,确保不同用户的登录记录不会相互影响。 - 隔离不同组的连续性:每个用户的连续登录天数是独立计算的,与其他用户无关。
- 按分组处理数据:例如,在分析用户连续登录时,需要按
示例:
SELECT
user_id,
login_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn
FROM login_log;
- 结果:每个
USER_ID
的LOGIN_DATE
会被单独排序并分配行号。
二、ORDER BY
的作用:确定顺序
- 功能:在每个分区内,按照指定的列排序,开窗函数会根据这个顺序计算结果。
- 在连续问题中的作用:
- 定义连续的方向:例如,按日期升序排列,才能识别出 “明天的日期是否等于今天 + 1”。
- 生成连续的行号:在差值法中,
ROW_NUMBER()
依赖ORDER BY
生成连续的行号,从而计算出正确的分组标识(GRP
)。
示例:
SELECT
user_id,
login_date,
login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS grp
FROM login_log;
- 结果:通过
ORDER BY login_date
确保日期连续递增,使得连续日期的login_date - ROW_NUMBER()
结果相同(即GRP
相同)。
三、连续问题中两者的协同作用
以 “查找用户连续登录日期段” 为例:
-
数据准备:
USER_ID | LOGIN_DATE --------------------- 1 | 2025-06-18 1 | 2025-06-19 1 | 2025-06-20 1 | 2025-06-22 2 | 2025-06-20 2 | 2025-06-21 2 | 2025-06-23
-
关键查询:
SELECT user_id, login_date, login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS grp FROM login_log;
-
计算过程:
PARTITION BY user_id
:将用户 1 和用户 2 的数据分开处理。ORDER BY login_date
:确保每个用户的日期按升序排列。- 计算
grp
:- 用户 1:
LOGIN_DATE | ROW_NUMBER | GRP (=LOGIN_DATE - ROW_NUMBER) ------------------------------------------------- 2025-06-18 | 1 | 2025-06-17 2025-06-19 | 2 | 2025-06-17 2025-06-20 | 3 | 2025-06-17 2025-06-22 | 4 | 2025-06-18
- 用户 2:
LOGIN_DATE | ROW_NUMBER | GRP -------------------------------- 2025-06-20 | 1 | 2025-06-19 2025-06-21 | 2 | 2025-06-19 2025-06-23 | 3 | 2025-06-20
- 用户 1:
-
分组结果:
- 用户 1:
GRP=2025-06-17
的日期(18-20 日)为连续段,GRP=2025-06-18
的日期(22 日)为单独段。 - 用户 2:同理,
GRP=2025-06-19
(20-21 日)和GRP=2025-06-20
(23 日)各为一段。
- 用户 1:
四、常见误区与注意事项
-
错误的
ORDER BY
:如果排序方向错误(如降序),会导致行号与日期顺序不匹配,无法正确识别连续段。-- 错误示例:按日期降序排列,导致行号与日期递增方向相反 login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date DESC) AS grp
-
缺失
PARTITION BY
:如果不分区,开窗函数会将所有用户的数据视为一个整体,导致不同用户的连续段被错误合并。-- 错误示例:缺少PARTITION BY,用户1和用户2的日期被混在一起 login_date - ROW_NUMBER() OVER (ORDER BY login_date) AS grp
五、总结
PARTITION BY
:将数据按组隔离,确保每个组内的连续性独立计算。ORDER BY
:定义数据的顺序,是识别连续值的基础(如日期递增、数值递增)。- 组合使用:通过
PARTITION BY
分组,再用ORDER BY
排序,开窗函数才能正确生成行号或比较前后值,从而识别出连续段。