文章目录
-
- [1. 基础概念](#1. 基础概念)
- [2. 常见窗口函数类型](#2. 常见窗口函数类型)
-
- [2.1 聚合类](#2.1 聚合类)
- [2.2 排名类](#2.2 排名类)
- [2.3 窗口滑动计算类](#2.3 窗口滑动计算类)
- [2.4 移动窗口类](#2.4 移动窗口类)
- [3. 实战案例](#3. 实战案例)
-
- [3.1 求每个部门薪资排名](#3.1 求每个部门薪资排名)
- [3.2 环比、同比](#3.2 环比、同比)
- [3.3 求累计和](#3.3 求累计和)
- [3.4 每个学生成绩占班级的百分比](#3.4 每个学生成绩占班级的百分比)
- [4. KPI 考核业务场景 --- 窗口函数 SQL 模板合集](#4. KPI 考核业务场景 — 窗口函数 SQL 模板合集)
-
- [4.1 计算员工月度累计销售额(KPI 基础统计)](#4.1 计算员工月度累计销售额(KPI 基础统计))
- [4.2 销售排名(同部门 Top 排名)](#4.2 销售排名(同部门 Top 排名))
- [4.3 环比增长率(本月 vs 上月)](#4.3 环比增长率(本月 vs 上月))
- [4.4 完成率统计(KPI vs 实际)](#4.4 完成率统计(KPI vs 实际))
- [4.5 团队贡献占比(员工 vs 部门总额)](#4.5 团队贡献占比(员工 vs 部门总额))
- [4.6 绩效分档(分位点计算)](#4.6 绩效分档(分位点计算))
- [4.7 平均值对比(部门平均 vs 个人业绩)](#4.7 平均值对比(部门平均 vs 个人业绩))
- [4.8 连续达标天数(运营 KPI)](#4.8 连续达标天数(运营 KPI))
- [4.9 当月 Top-N 员工(带子查询过滤)](#4.9 当月 Top-N 员工(带子查询过滤))
- [5. 优缺点](#5. 优缺点)
本文总结整理了一份 SQL 窗口函数(Window Function)终极指南,适用于复杂统计、简化 SQL 的应用场景,结合应用场景分析了其优缺点。
1. 基础概念
窗口函数是在 SELECT 查询结果集的每一行上执行聚合/排名/计算 的函数。
与普通聚合函数(SUM、AVG、COUNT 等)不同,窗口函数 不会收缩结果集,而是为每一行增加一个计算结果。
语法:
sql
function_name(expression) OVER (
PARTITION BY col1, col2
ORDER BY col3
ROWS BETWEEN ...
)
-
PARTITION BY:对数据分组(类似 GROUP BY,但不会折叠行)
-
ORDER BY:定义计算顺序
-
ROWS BETWEEN:定义窗口范围(如前 N 行、到当前行为止)
2. 常见窗口函数类型
2.1 聚合类
在分组基础上给每行都输出一个结果:
sql
SUM(sales) OVER(PARTITION BY region) AS region_sales,
AVG(sales) OVER(PARTITION BY region) AS region_avg
- 每一行都能看到所属分区的总销售额/平均值。
2.2 排名类
sql
ROW_NUMBER() OVER(PARTITION BY region ORDER BY sales DESC) AS rn,
RANK() OVER(PARTITION BY region ORDER BY sales DESC) AS rnk,
DENSE_RANK() OVER(PARTITION BY region ORDER BY sales DESC) AS drnk
-
ROW_NUMBER:严格递增,不考虑并列
-
RANK:有并列时跳号
-
DENSE_RANK:有并列但不跳号
2.3 窗口滑动计算类
sql
LAG(sales, 1) OVER(ORDER BY month) AS prev_sales,
LEAD(sales, 1) OVER(ORDER BY month) AS next_sales,
sales - LAG(sales, 1) OVER(ORDER BY month) AS sales_diff
- 常用于环比、同比计算。
2.4 移动窗口类
sql
AVG(sales) OVER(ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
- 最近三个月销售额的滑动平均。
3. 实战案例
3.1 求每个部门薪资排名
sql
SELECT emp_id, dept_id, salary,
RANK() OVER(PARTITION BY dept_id ORDER BY salary DESC) AS dept_rank
FROM employees;
3.2 环比、同比
sql
SELECT month, sales,
sales - LAG(sales, 1) OVER(ORDER BY month) AS mom_growth,
sales - LAG(sales, 12) OVER(ORDER BY month) AS yoy_growth
FROM sales_data;
3.3 求累计和
sql
SELECT month, sales,
SUM(sales) OVER(ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM sales_data;
3.4 每个学生成绩占班级的百分比
sql
SELECT student_id, score,
score * 100.0 / SUM(score) OVER(PARTITION BY class_id) AS pct
FROM scores;
4. KPI 考核业务场景 --- 窗口函数 SQL 模板合集
4.1 计算员工月度累计销售额(KPI 基础统计)
sql
SELECT
emp_id,
emp_name,
month,
SUM(sales_amount) OVER (PARTITION BY emp_id ORDER BY month) AS cum_sales
FROM sales;
📌 场景:考核每个员工的月度累计销售额,用于判断增长趋势。
4.2 销售排名(同部门 Top 排名)
sql
SELECT
emp_id,
emp_name,
dept_id,
month,
sales_amount,
RANK() OVER (PARTITION BY dept_id, month ORDER BY sales_amount DESC) AS sales_rank
FROM sales;
📌 场景:考核月度销售冠军,支持并列名次(RANK vs DENSE_RANK vs ROW_NUMBER)。
4.3 环比增长率(本月 vs 上月)
sql
SELECT
emp_id,
month,
sales_amount,
LAG(sales_amount, 1, 0) OVER (PARTITION BY emp_id ORDER BY month) AS prev_sales,
ROUND(
(sales_amount - LAG(sales_amount, 1, 0) OVER (PARTITION BY emp_id ORDER BY month))
/ NULLIF(LAG(sales_amount, 1, 0) OVER (PARTITION BY emp_id ORDER BY month), 0) * 100, 2
) AS growth_rate
FROM sales;
📌 场景:考核员工销售额环比增长(同比同理,替换 LAG(...,12))。
4.4 完成率统计(KPI vs 实际)
sql
SELECT
emp_id,
month,
target_amount,
SUM(sales_amount) OVER (PARTITION BY emp_id, month) AS total_sales,
ROUND(SUM(sales_amount) OVER (PARTITION BY emp_id, month) / target_amount * 100, 2) AS completion_rate
FROM sales_targets;
📌 场景:考核员工 KPI 目标完成情况。
4.5 团队贡献占比(员工 vs 部门总额)
sql
SELECT
emp_id,
emp_name,
dept_id,
month,
sales_amount,
ROUND(sales_amount * 100.0 / SUM(sales_amount) OVER (PARTITION BY dept_id, month), 2) AS contribution_pct
FROM sales;
📌 场景:考核某员工对部门整体业绩的贡献度。
4.6 绩效分档(分位点计算)
sql
SELECT
emp_id,
emp_name,
month,
sales_amount,
NTILE(4) OVER (PARTITION BY month ORDER BY sales_amount DESC) AS quartile
FROM sales;
📌 场景:把员工销售分为四档(Q1 高绩效 → Q4 低绩效),用于绩效考核。
4.7 平均值对比(部门平均 vs 个人业绩)
sql
SELECT
emp_id,
emp_name,
dept_id,
month,
sales_amount,
AVG(sales_amount) OVER (PARTITION BY dept_id, month) AS dept_avg,
sales_amount - AVG(sales_amount) OVER (PARTITION BY dept_id, month) AS diff_from_avg
FROM sales;
📌 场景:员工业绩与部门均值对比,衡量超额/不足。
4.8 连续达标天数(运营 KPI)
sql
SELECT
emp_id,
work_date,
kpi_value,
SUM(CASE WHEN kpi_value >= target THEN 1 ELSE 0 END)
OVER (PARTITION BY emp_id ORDER BY work_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS consecutive_days
FROM daily_kpi;
📌 场景:考核运营人员 KPI 连续达标天数。
4.9 当月 Top-N 员工(带子查询过滤)
sql
SELECT *
FROM (
SELECT
emp_id,
emp_name,
month,
sales_amount,
ROW_NUMBER() OVER (PARTITION BY month ORDER BY sales_amount DESC) AS rn
FROM sales
) t
WHERE rn <= 3;
📌 场景:考核月度前三名员工。
5. 优缺点
优缺点 | 内容 |
---|---|
优点 | 简化 SQL:不用子查询/自连接即可实现复杂统计。增强分析能力:轻松实现排名、累计、环比、同比。结果集完整:不会折叠行,统计与明细可共存。性能好:数据库优化器对窗口函数优化较好,尤其在 OLAP 场景。 |
缺点 | 学习曲线:语法复杂,理解成本高。性能风险:对大表、复杂 ORDER BY 的窗口函数,可能产生大规模排序和内存消耗。移植性差:不同数据库的窗口函数支持范围不同(如 MySQL 8 才支持,Oracle/PG/SQL Server 更完善)。调试难度:结果往往依赖分区、排序,稍有疏忽结果就偏差。 |

"人的一生会经历很多痛苦,但回头想想,都是传奇"。