让理解窗口函数变得简单

我写了一篇关于理解窗口函数的文章。

吐槽一下 :写这篇文章不是为了背具体的 API 名字。在国内的面试环境中,人们往往期待你能够复述出准确的函数或方法。但对我这样一个更偏向理解和推理的人来说,其实我完全可以在不死记硬背的情况下写出需要的代码 。真正重要的是掌握底层逻辑,并能解决实际问题,而不是仅仅记住一个定义。

我从来不会鼓励所谓的盲目走别人走过的路,大部分人坚持的也不一定是对的。

这篇文章更多是关于如何重塑我对技术概念的表达方式。我想要致敬那个曾经的自己------那个在死记硬背的泥沼里挣扎的自己------并展示出:其实有另一种方式,可以按照自己的理解去解释和学习技术。

每当我重新诠释并分享所谓的"标准答案"时,我不仅仅是在分享知识,我也是在为过去那个因为记忆力不足而被误解的自己正名

我希望别人能明白,学习技术并不是为了背名词------那些随时可以用搜索引擎查到。真正的核心是:你能不能理解、应用并解决问题。这才是技术的本质。


总结:

✅ 小技巧:别去死记函数,按类别去思考:

  • 想要顺序/排名 → 用 位置类函数
  • 想要累计和/滑动平均 → 用 聚合类函数
  • 想要邻居信息 → 用 访问其他行的函数
  • 想要分布/百分比 → 用 比例类函数

窗口函数简化理解

关键点: 你不需要记住所有函数名,但你必须知道 什么时候用窗口函数,为什么用

窗口函数描述的是:一行数据要么和它的邻居有关,要么和整个数据集有关

我喜欢把它们分成 4 个方向:看邻居、看临近区域、看序列顺序、看整体分布。


吐槽一下:我想先把结论抛出来

✅ 小技巧:不要死记函数,按类别去思考:

  • "我想排名 → 用 位置类函数"
  • "我想累计求和 → 用 累计/移动聚合函数"
  • "我想看邻居 → 用 LAG/LEAD"
  • "我想看分布/百分比 → 用 累计/比例函数"

四大类别

1️⃣ 位置指标(Position / Ranking)

关注点:序列里的顺序 / 排名 / 分桶

  • 例子:row_numberrankdense_rankntile

2️⃣ 累计 / 移动聚合(Aggregation / Moving / Cumulative)

关注点:从开始到当前行的累计,或者在一个滑动窗口里的聚合

  • 例子:累计和 (cumulative sum),移动平均 (moving average)

3️⃣ 访问其他行(Accessing Other Rows)

关注点:不做聚合,只是看看邻居的数据

  • 例子:上一行 (LAG),下一行 (LEAD)

4️⃣ 比例 / 分布(Proportion / Distribution)

关注点:一个值在整体数据中的位置 / 分布

  • 例子:累计分布 (cumulative distribution),百分位排名 (percent rank)

窗口函数在 SQL 中的执行顺序

可以这样理解 SQL 的逻辑执行顺序:

1️⃣ FROM / JOIN → 拿到数据表

2️⃣ WHERE → 过滤行

3️⃣ GROUP BY → 分组

4️⃣ HAVING → 对分组结果再过滤

5️⃣ 窗口函数 (OVER) → 在组或整张表上计算

(⚠️ 注意:在 GROUP BY 之后,但在 SELECT 阶段完成,不是在 ORDER BY 之后)

6️⃣ SELECT → 选择列

7️⃣ ORDER BY → 排序

8️⃣ LIMIT → 限制返回的行数


示例表

sql 复制代码
CREATE TABLE Queue (
  turn INT,
  person_name VARCHAR(50),
  weight INT
);

INSERT INTO Queue (turn, person_name, weight) VALUES
(1, 'Alice', 250),
(2, 'Alex', 350),
(3, 'John Cena', 400),
(4, 'Marie', 200),
(5, 'Bob', 175),
(6, 'Winston', 500);

1️⃣ 累计 / 移动聚合

sql 复制代码
SELECT
  turn,
  person_name,
  weight,
  SUM(weight) OVER (ORDER BY turn ROWS UNBOUNDED PRECEDING) AS total_weight,
  AVG(weight) OVER (ORDER BY turn ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM Queue;
  • total_weight:从第一行到当前行的累计和
  • moving_avg:当前行 + 前两行的平均值

2️⃣ 排名 / 顺序

sql 复制代码
SELECT
    person_name,
    weight,
    ROW_NUMBER() OVER (ORDER BY weight DESC) AS row_num,
    RANK() OVER (ORDER BY weight DESC) AS rank_by_weight,
    DENSE_RANK() OVER (ORDER BY weight DESC) AS dense_rank,
    NTILE(3) OVER (ORDER BY weight) AS ntile_3
FROM Queue;
  • row_num:顺序编号
  • rank_by_weight:按体重排名,有并列会跳过名次
  • dense_rank:按体重排名,有并列但不跳过名次
  • ntile_3:分成 3 个桶

3️⃣ 访问其他行

sql 复制代码
SELECT
  person_name,
  weight,
  LAG(weight, 1) OVER (ORDER BY turn) AS prev_weight,
  LEAD(weight, 1) OVER (ORDER BY turn) AS next_weight
FROM Queue;
  • prev_weight:上一行的体重
  • next_weight:下一行的体重

4️⃣ 累计 / 百分比 / 分布

sql 复制代码
SELECT
  person_name,
  weight,
  CUME_DIST() OVER (ORDER BY weight) AS weight_cum_dist,
  PERCENT_RANK() OVER (ORDER BY weight) AS weight_percent_rank,
  NTILE(3) OVER (ORDER BY weight) AS ntile_3
FROM Queue;
  • CUME_DIST():当前行在队列中所占比例(累计分布)
  • PERCENT_RANK():百分位排名
  • NTILE(3):分成 3 个桶
相关推荐
王码码20358 小时前
Go语言的测试:从单元测试到集成测试
后端·golang·go·接口
王码码20358 小时前
Go语言中的测试:从单元测试到集成测试
后端·golang·go·接口
嵌入式×边缘AI:打怪升级日志9 小时前
使用JsonRPC实现前后台
前端·后端
小码哥_常9 小时前
从0到1:Spring Boot 中WebSocket实战揭秘,开启实时通信新时代
后端
lolo大魔王10 小时前
Go语言的异常处理
开发语言·后端·golang
IT_陈寒12 小时前
Python多进程共享变量那个坑,我差点没爬出来
前端·人工智能·后端
码事漫谈12 小时前
2026软考高级·系统架构设计师备考指南
后端
AI茶水间管理员13 小时前
如何让LLM稳定输出 JSON 格式结果?
前端·人工智能·后端
其实是白羊14 小时前
我用 Vibe Coding 搓了一个 IDEA 插件,复制URI 再也不用手动拼了
后端·intellij idea
用户83562907805114 小时前
Python 操作 Word 文档节与页面设置
后端·python