【SQL】SQL-常见窗口函数有哪些-上篇

【SQL】SQL-常见窗口函数有哪些-上篇

一、引言

今天来聊聊 Hive 里窗口函数的应用场景,原来大家对SQL这么感兴趣,阅读比Python高好多.orz

【窗口函数,绝对SQL进阶的关键门槛。SQL 水平能否上一个台阶,除了思维,更重要的是高效运用窗口函数。】

很多同学写聚合查询时只会 GROUP BY其实窗口函数才是真爱啊,这个浮躁的社会。

窗口既能分组聚合,又不会丢失明细数据,还能轻松实现排名、累计、同比等高阶分析,妥妥的。

Hive 从 2.0 开始支持窗口函数,咱们先讲上篇------聚合类窗口函数排名类窗口函数,配合实战解析,保证你看完就能用上。


二、窗口函数基础概念

在正式讲之前,先搞清楚几个关键概念,窗口函数的基础:

概念 说明 示例
OVER() 窗口函数的声明,定义计算窗口 SUM(col) OVER(...)
PARTITION BY 分区,类似 GROUP BY,但保留明细 按部门分组统计
ORDER BY 窗口内排序,决定计算顺序 按时间正序
ROWS BETWEEN 窗口边界,控制参与计算的行 往前3行~当前行
sql 复制代码
-- 窗口函数基本语法
SELECT 
    SUM(column) OVER( PARTITION BY col1, col2  -- 可选:分区
    ORDER BY col3            -- 可选:排序
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 可选:窗口边界
) AS 窗口函数基本语法

是不是看着很难理解?没事,确实也不简单,orz...慢慢来~


三、聚合类窗口函数(理解了,前面都是坦途!)

1. SUM 累计求和

基础用法

sql 复制代码
-- 简单累计:所有数据求和
SELECT
    name,
    score,
    SUM(score) OVER() AS 总分
FROM student_scores;

划重点:OVER() 不带任何参数时,窗口是全部数据,等同于 GROUP BY 的聚合效果,但保留每行明细。

带分区累计

sql 复制代码
-- 按班级分组计算累计
SELECT
    class,
    name,
    score,
    SUM(score) OVER(PARTITION BY class) AS 班级总分,
    ROUND(score * 100.0 / SUM(score) OVER(PARTITION BY class), 2) AS 班级占比
FROM student_scores;

带排序的滑动窗口

sql 复制代码
-- 按时间顺序累计(滚动求和)
SELECT
    dt,
    sales,
    SUM(sales) OVER(ORDER BY dt ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 累计销售额
FROM daily_sales
ORDER BY dt;

划重点:ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 表示从第一行到当前行,这是滑动窗口求累计的标配写法。


2. AVG 平均值

sql 复制代码
-- 计算每人的平均月度消费
SELECT
    user_id,
    month,
    consumption,
    AVG(consumption) OVER(PARTITION BY user_id ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS 近3月平均   -- 包含当前月和前2个月
FROM user_consumption
ORDER BY user_id, month;

划重点:滑动窗口 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 实现了"近3个月平均"的效果,比 GROUP BY 灵活多了。


3. COUNT 计数

sql 复制代码
-- 统计每笔订单之后还有多少笔订单   是不是比你们写一堆的LEFT JOIN 省力多了?
SELECT
    order_id,
    create_time,
    amount,
    COUNT(*) OVER( ORDER BY create_time ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS 后续订单数   -- 当前行之后所有行
FROM orders
ORDER BY create_time;

四、排名类窗口函数(面试有笔试的话,必考!)

1. ROW_NUMBER / RANK / DENSE_RANK 详解

这三个函数都是排名,但有区别:

函数 特点 示例(同样是1,2,2,3)
ROW_NUMBER 连续不重复编号 1, 2, 3, 4
RANK 并列跳号 1, 2, 2, 4
DENSE_RANK 并列不跳号 1, 2, 2, 3
sql 复制代码
-- 对比三种排名
SELECT
    name,
    score,
    ROW_NUMBER() OVER(ORDER BY score DESC) AS 行号,
    RANK() OVER(ORDER BY score DESC)       AS 普通排名,
    DENSE_RANK() OVER(ORDER BY score DESC) AS 紧凑排名
FROM student_scores
ORDER BY score DESC;

输出示例:

复制代码
name | score | 行号 | 普通排名 | 紧凑排名
张三 | 95    | 1    | 1        | 1
李四 | 90    | 2    | 2        | 2
王五 | 90    | 3    | 2        | 2
赵六 | 85    | 4    | 4        | 3

划重点:分数相同时,ROW_NUMBER 给不同编号,RANK 编号相同但下一个跳号,DENSE_RANK 编号相同且下一个不跳号。


2. 实战:班级内排名

sql 复制代码
-- 给每个班级的学生按成绩排名
SELECT
    class,
    name,
    score,
    ROW_NUMBER() OVER(PARTITION BY class ORDER BY score DESC) AS 班级排名,
    RANK() OVER(PARTITION BY class ORDER BY score DESC)       AS 班级排名_并列
FROM student_scores
ORDER BY class, 班级排名;

3. 实战:筛选TopN

sql 复制代码
-- 取出每个班级成绩前3名
SELECT *
FROM (
    SELECT
        class,
        name,
        score,
        ROW_NUMBER() OVER(PARTITION BY class ORDER BY score DESC) AS rn
    FROM student_scores
) t
WHERE rn <= 3;

划重点:窗口函数不能直接在 WHERE 里用,得套一层子查询来实现筛选。


4. 实战:连续出现N次 -- 【连续一出,面试基本能挂掉90%的人】

sql 复制代码
-- 找出连续3天以上活跃的用户
WITH user_active AS (
    SELECT DISTINCT
        user_id,
        dt
    FROM user_log
)
SELECT
    user_id,
    MIN(dt) AS 连续开始,
    MAX(dt) AS 连续结束,
    COUNT(*) AS 连续天数
FROM (
    SELECT
        user_id,
        dt,
        -- 把不连续的日期分组
        dt - ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY dt) * 1 AS grp
    FROM user_active
) t
GROUP BY user_id, grp
HAVING COUNT(*) >= 3
ORDER BY user_id, 连续开始;

这是经典的"连续天数"问题解法:通过 dt - rn 把连续日期归到同一组。


五、总结对比

函数类别 常用函数 典型场景
聚合类 SUM, AVG, COUNT, MAX, MIN 累计、占比、滑动平均
排名类 ROW_NUMBER, RANK, DENSE_RANK 名次、TopN、连续天数

怎么样,窗口函数很好用吧~!关键是理解、属于数据结构中,如何构建结构的空间思维。

下篇预告: 偏移类窗口函数(LAG/LEAD/FIRST_VALUE/LAST_VALUE)以及综合实战案例,敬请期待!

今天的分享就到这里,觉得有用动动小手点赞+关注+收藏,一键三连~ 有问题留言沟通啦~

相关推荐
pele2 小时前
如何处理ORA-01152报错_恢复未完成导致的数据文件仍需介质恢复
jvm·数据库·python
IntMainJhy2 小时前
【flutter for open harmony】Flutter SQLite 本地数据库的鸿蒙化适配与实战指南
数据库·flutter·sqlite
qq_372154232 小时前
SQL如何避免隐式类型转换导致的慢查询_参数类型对齐与索引失效
jvm·数据库·python
qq_342295822 小时前
MySQL怎样在触发器中引用新旧数据行_NEW与OLD关键字详解
jvm·数据库·python
m0_746752302 小时前
如何配置Data Guard主备库目录结构不同_DB_FILE_NAME_CONVERT参数转换规则
jvm·数据库·python
weixin_424999362 小时前
CSS如何解决CSS冲突导致的BUG_使用CSS层叠层特性隔离样式
jvm·数据库·python
2301_816660212 小时前
将地址转换为可点击的 Google Maps 链接(支持动态生成)
jvm·数据库·python
步辞2 小时前
rowspan属性跨页表格怎么处理_打印断页兼容建议【方法】
jvm·数据库·python
weixin_458580122 小时前
Python模型部署怎么做_FastAPI封装模型为RESTful接口
jvm·数据库·python