精选·Hive与SQL

窗口函数有哪几类?row_number、rank、lag 各适合什么场景?

91学AI·2026/7/13·8 阅读

考察点

窗口函数是 SQL 手撕环节的必考内容,面试官想看你是否真正理解「窗口」这个概念,而不是背函数名。追问方向:row_number 和 rank 的区别、窗口帧怎么定义、窗口函数和普通 group by 的本质差异。

参考答案

窗口函数的本质:分组但不折叠

普通 group by 会把每组折叠成一行,组内细节丢了。窗口函数是对每一行都返回一个值,但这个值的计算范围是一个「窗口」——由 over() 里定义的一组行。语法骨架:

func() over (
  partition by 分组列        -- 把数据切成若干分区,类似 group by
  order by 排序列            -- 分区内排序
  rows between 起点 and 终点  -- 窗口帧,可选
)

三个子句各司其职:partition by 决定和谁一组,order by 决定组内顺序(排序类函数必须有),窗口帧决定计算范围(默认是分区起点到当前行)。

四类窗口函数

1. 排序类row_number()rank()dense_rank()

  • row_number:分区内从 1 开始连续编号,相同值也给不同名次(顺序不确定)。
  • rank:相同值同名次,后续名次跳号,1、2、2、4。
  • dense_rank:相同值同名次,后续名次不跳号,1、2、2、3。

面试最爱考的区别就在这。取每组第一名用哪个都行,取「前三名」时 rank 可能带出 4 个人(有并列第三时),dense_rank 保证恰好三个名次段位,业务上「销售额前三的品类」如果允许并列要用 rank,严格控制人数用 row_number。

2. 取值类lag()lead()first_value()last_value()

lag(col, 1) 取当前行上一行的 col,lead 取下一行,offset 和默认值可配。环比、留存、连续行为分析全靠它们。first_value/last_value 取窗口帧内首尾值,注意last_value 要配窗口帧才有直觉语义,因为默认帧只到当前行,last_value(col) over (partition by g order by t rows between unbounded preceding and unbounded following) 才是「组内最后一行的值」。

3. 聚合类:sum、count、avg、max、min 直接当窗口函数用

sum(amount) over (partition by user_id order by dt
  rows between unbounded preceding and current row)  -- 累计求和
avg(amount) over (partition by user_id order by dt
  rows between 6 preceding and current row)          -- 7 天移动平均

聚合类配合窗口帧是核心玩法:累计值、移动平均、组内占比(amount / sum(amount) over (partition by dept))。

4. 分布类ntile(n)cume_dist()percent_rank()

ntile(4) 把分区均匀切成 4 桶标 1-4,做用户分层(RFM 里的分位打分)常用。cume_dist 返回累计分布占比,percent_rank 是标准化排名。考频低但提一句能加分。

三大高频实战场景

TopNselect * from (select *, row_number() over (partition by dept order by salary desc) rn from emp) t where rn <= 3。这是面试出镜率最高的窗口函数写法。

环比/同比amount / lag(amount) over (partition by shop_id order by month) - 1 算月环比。

去重where row_number() over (partition by 主键 order by 更新时间 desc) = 1 保留每个主键最新一条,CDC 数据入仓的标准写法。

一个容易忽略的执行细节

同一条 SQL 里多个窗口函数如果 over 子句不同,引擎会为每种不同的 partition/order 组合各做一次 shuffle 和排序。写复杂 SQL 时尽量让窗口函数的 partition by + order by 保持一致,能合并成一次 shuffle,性能差别在大表上非常明显。

可能的追问

  • 窗口函数和 group by 能一起用吗? 能。执行顺序是 where → group by → having → 窗口函数 → select → order by,窗口函数是在分组聚合之后算的,所以 sum(sum(amount)) over (...) 这种嵌套是合法的——内层是分组聚合,外层是窗口聚合。
  • rank 有并列时取前三名怎么保证只有三人? 用 row_number 不用 rank;或者明确业务语义「允许并列」用 rank 并接受人数超编。面试时主动问清这个口径是加分项。
  • 窗口函数为什么慢? 每个不同的窗口规格都要一次全分区 shuffle + 排序,partition by 的 key 倾斜时同样会数据倾斜。大表上可以先过滤再开窗口,减少参与排序的数据量。

评论 (0)

暂无评论,快来抢沙发吧!

91学AI

© 2026 91学AI · 按岗位学 AI 与大数据. All rights reserved.