让理解窗口函数变得简单

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

吐槽一下 :写这篇文章不是为了背具体的 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 个桶
相关推荐
小蒜学长3 小时前
springboot海洋馆预约系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
IT_陈寒4 小时前
SpringBoot实战:这5个隐藏技巧让我开发效率提升200%,90%的人都不知道!
前端·人工智能·后端
catchadmin4 小时前
如何在 PHP 升级不踩坑?学会通过阅读 RFC 提前预知版本变化
开发语言·后端·php
风象南4 小时前
商业化必备:SpringBoot 实现许可证控制
后端
caibixyy12 小时前
Spring Boot 整合 Redisson 实现分布式锁:实战指南
spring boot·分布式·后端
码事漫谈12 小时前
C++编程陷阱:悬空引用检测方法与防范指南
后端
码事漫谈12 小时前
缓存友好的数据结构设计:提升性能的关键技巧
后端
sheji341613 小时前
【开题答辩全过程】以 springboot高校社团管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
聆风吟º14 小时前
远程录制新体验:Bililive-go与cpolar的无缝协作
开发语言·后端·golang