大家好,我是船长。今天聊一个数据分析里最容易被问到的问题:SQL窗口函数。很多人在面试的时候会被问到,但真正用过的人其实不多。今天船长把实际工作中最常用的5个场景完整代码分享出来。
01 什么是窗口函数?
先说基础概念。
普通聚合函数(如SUM、AVG、COUNT)会把多行聚合成一行。
窗口函数不一样。它保留原有行,同时给每一行加上"窗口"计算的结果。
简单说:窗口函数 = 不减少行数的聚合计算
基本语法:
SELECT 字段, 聚合函数 OVER (PARTITION BY 分组 ORDER BY 排序) FROM 表
02 场景1:计算累计销售额
这个场景太常见了。
按日期看每天的销售额,同时想知道"到这一天为止累计卖了多少钱"。
SELECT order_date, daily_sales, SUM(daily_sales) OVER ( ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS cumulative_sales FROM sales_daily
关键点:
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
意思是:从第一行到当前行。这句决定了"窗口"的范围。
03 场景2:计算同比/环比增长率
有了窗口函数,计算增长率变得很简单。
SELECT month, sales, LAG(sales, 1) OVER (ORDER BY month) AS last_month_sales, ROUND( (sales - LAG(sales, 1) OVER (ORDER BY month)) / LAG(sales, 1) OVER (ORDER BY month) * 100, 2 ) AS mom_growth_pct FROM monthly_sales
**LAG函数:**取当前行之前的第N行数据。这里用LAG(sales, 1)取上月销售额。
04 场景3:分组排名
按部门分组计算员工排名。
SELECT department, employee_name, sales, RANK() OVER ( PARTITION BY department ORDER BY sales DESC ) AS dept_rank FROM employee_sales
**PARTITION BY:**按部门分组后再排序,每个部门独立排名。
**RANK():**如果有并列,会跳号(1,1,3)。如果需要不跳号,用DENSE_RANK()。
05 场景4:计算移动平均值
消除数据波动,看趋势的时候特别有用。
SELECT order_date, daily_sales, ROUND( AVG(daily_sales) OVER ( ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ), 2 ) AS ma_7d FROM sales_daily
**ROWS BETWEEN 6 PRECEDING AND CURRENT ROW:**当前行 + 往前6行 = 7天窗口。算出来的就是7日移动平均。
06 场景5:计算留存率
这是增长分析的灵魂。
WITH first_login AS ( SELECT user_id, MIN(login_date) AS first_date FROM user_logins GROUP BY user_id ), retention AS ( SELECT fl.user_id, DATEDIFF(ul.login_date, fl.first_date) AS days_since_first FROM user_logins ul JOIN first_login fl ON ul.user_id = fl.user_id ) SELECT days_since_first, COUNT(DISTINCT user_id) AS retained_users, ROUND( COUNT(DISTINCT user_id) * 100.0 / (SELECT COUNT(DISTINCT user_id) FROM first_login), 2 ) AS retention_rate FROM retention GROUP BY days_since_first ORDER BY days_since_first
这个查询会输出每天的留存用户数和留存率。
07 常用窗口函数速查
聚合类窗口函数:
SUM() / AVG() / COUNT() / MAX() / MIN()
导航类窗口函数:
LAG(col, N) — 取前N行
LEAD(col, N) — 取后N行
排名类窗口函数:
ROW_NUMBER() — 1,2,3,4(不并列)
RANK() — 1,1,3(并列跳号)
DENSE_RANK() — 1,1,2(并列不跳号)
分布类窗口函数:
NTILE(N) — 把数据分成N组
PERCENT_RANK() — 计算百分位排名
记住:窗口函数的核心是OVER子句。理解OVER怎么用,就理解了一切。
有问题评论区见。