本练习专为Oracle 开窗函数(分析函数) 设计,覆盖排名、聚合、分区、行范围、序列值 5 大核心场景,包含基础题、进阶题、综合实战题 3 个难度梯度,附完整表数据、题目、答案、结果解析,适合自学、面试备考、技能巩固。
适用人群 :Oracle 开发 / 测试 / 运维、SQL 进阶学习者核心知识点 :ROW_NUMBER()/RANK()/DENSE_RANK()、SUM()/AVG()/COUNT() 开窗、PARTITION BY、ORDER BY、ROWS/RANGE、LAG()/LEAD()、NTILE()
一、练习数据表(可直接执行创建)
1. 创建测试表 + 插入数据
执行以下 SQL,生成练习用的 员工销售业绩表(sale_employee)
sql
-- 创建员工销售表
CREATE TABLE sale_employee (
emp_id NUMBER(5) PRIMARY KEY, -- 员工ID
emp_name VARCHAR2(20), -- 员工姓名
dept_name VARCHAR2(20), -- 部门名称
sale_month VARCHAR2(10), -- 销售月份
sale_amt NUMBER(10,2) -- 销售金额
);
-- 插入测试数据
INSERT INTO sale_employee VALUES (101, '张三', '销售一部', '2025-01', 50000);
INSERT INTO sale_employee VALUES (102, '李四', '销售一部', '2025-01', 45000);
INSERT INTO sale_employee VALUES (103, '王五', '销售一部', '2025-01', 50000);
INSERT INTO sale_employee VALUES (104, '赵六', '销售二部', '2025-01', 60000);
INSERT INTO sale_employee VALUES (105, '钱七', '销售二部', '2025-01', 55000);
INSERT INTO sale_employee VALUES (106, '孙八', '销售三部', '2025-01', 38000);
INSERT INTO sale_employee VALUES (101, '张三', '销售一部', '2025-02', 52000);
INSERT INTO sale_employee VALUES (102, '李四', '销售一部', '2025-02', 48000);
INSERT INTO sale_employee VALUES (104, '赵六', '销售二部', '2025-02', 62000);
INSERT INTO sale_employee VALUES (106, '孙八', '销售三部', '2025-02', 40000);
COMMIT;
2. 表数据预览
表格
| emp_id | emp_name | dept_name | sale_month | sale_amt |
|---|---|---|---|---|
| 101 | 张三 | 销售一部 | 2025-01 | 50000.00 |
| 102 | 李四 | 销售一部 | 2025-01 | 45000.00 |
| 103 | 王五 | 销售一部 | 2025-01 | 50000.00 |
| 104 | 赵六 | 销售二部 | 2025-01 | 60000.00 |
| 105 | 钱七 | 销售二部 | 2025-01 | 55000.00 |
| 106 | 孙八 | 销售三部 | 2025-01 | 38000.00 |
| 101 | 张三 | 销售一部 | 2025-02 | 52000.00 |
| 102 | 李四 | 销售一部 | 2025-02 | 48000.00 |
| 104 | 赵六 | 销售二部 | 2025-02 | 62000.00 |
| 106 | 孙八 | 销售三部 | 2025-02 | 40000.00 |
二、分难度练习题
基础题(5 道,核心语法入门)
题目 1
查询所有员工 2025-01 月销售数据,按销售金额降序排名 ,展示连续排名(无并列、无跳跃)。考察:ROW_NUMBER()
题目 2
查询所有员工 2025-01 月销售数据,按销售金额降序排名 ,展示并列排名(相同金额名次相同,后续跳跃)。考察:RANK()
题目 3
查询所有员工 2025-01 月销售数据,按销售金额降序排名 ,展示并列排名(相同金额名次相同,后续不跳跃)。考察:DENSE_RANK()
题目 4
按部门分区 ,统计每个部门的总销售额、平均销售额 ,每行数据都展示对应部门的聚合值。考察:SUM()/AVG() OVER(PARTITION BY)
题目 5
将所有员工按销售金额平均分成 3 组 ,展示每组数据。考察:NTILE()
进阶题(5 道,分区 + 排序 + 范围)
题目 6
按部门 + 月份 双维度分区,查询每个员工在本部门本月的销售排名 (DENSE_RANK)。考察:PARTITION BY 多字段
题目 7
查询 2025-01 月数据,按销售金额升序,计算累计销售额 (逐行累加)。考察:SUM() OVER(ORDER BY ROWS BETWEEN...)
题目 8
查询每个员工的当月销售额 和上月销售额 ,无上月数据显示 NULL。考察:LAG() 偏移函数
题目 9
查询每个部门销售额最高的 2 名员工 (不考虑并列)。考察:ROW_NUMBER() + 子查询过滤
题目 10
统计每个员工的销售额,与本部门最高销售额、最低销售额 的差值。考察:MAX()/MIN() OVER(PARTITION BY)
综合实战题(3 道,业务场景落地)
题目 11
统计每个部门每月 的:总销售额、环比增长率(本月 - 上月)/ 上月 * 100%,保留 2 位小数。考察:PARTITION BY + LAG() + 四则运算
题目 12
筛选出连续两个月都有销售记录 的员工,展示员工姓名、两个月销售额。考察:LEAD() + 条件过滤
题目 13
查询每个部门内,销售额超过部门平均值 的员工,展示员工信息、个人销售额、部门平均值。考察:AVG() OVER() + 条件过滤
三、完整答案 + 结果解析
基础题答案
题目 1
sql
SELECT
emp_name, sale_amt,
ROW_NUMBER() OVER(ORDER BY sale_amt DESC) AS 连续排名
FROM sale_employee WHERE sale_month = '2025-01';
解析:相同金额也会分配不同名次,结果唯一。
题目 2
sql
SELECT
emp_name, sale_amt,
RANK() OVER(ORDER BY sale_amt DESC) AS 并列跳跃排名
FROM sale_employee WHERE sale_month = '2025-01';
解析:张三、王五同第 1,李四直接第 4。
题目 3
sql
SELECT
emp_name, sale_amt,
DENSE_RANK() OVER(ORDER BY sale_amt DESC) AS 并列连续排名
FROM sale_employee WHERE sale_month = '2025-01';
解析:张三、王五同第 1,李四第 3,无跳跃。
题目 4
sql
SELECT
emp_name, dept_name, sale_amt,
SUM(sale_amt) OVER(PARTITION BY dept_name) AS 部门总销售额,
AVG(sale_amt) OVER(PARTITION BY dept_name) AS 部门平均销售额
FROM sale_employee;
解析:同一部门所有行展示相同的聚合值。
题目 5
sql
SELECT
emp_name, sale_amt,
NTILE(3) OVER(ORDER BY sale_amt DESC) AS 分组编号
FROM sale_employee;
解析:数据按排序平均分配到 3 个组。
进阶题答案
题目 6
sql
SELECT
emp_name, dept_name, sale_month, sale_amt,
DENSE_RANK() OVER(PARTITION BY dept_name, sale_month ORDER BY sale_amt DESC) AS 部门月度排名
FROM sale_employee;
题目 7
sql
SELECT
emp_name, sale_amt,
SUM(sale_amt) OVER(ORDER BY sale_amt ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 累计销售额
FROM sale_employee WHERE sale_month = '2025-01';
题目 8
sql
SELECT
emp_name, sale_month, sale_amt,
LAG(sale_amt, 1, NULL) OVER(PARTITION BY emp_id ORDER BY sale_month) AS 上月销售额
FROM sale_employee;
题目 9
sql
SELECT * FROM (
SELECT
emp_name, dept_name, sale_amt,
ROW_NUMBER() OVER(PARTITION BY dept_name ORDER BY sale_amt DESC) AS rn
FROM sale_employee
) WHERE rn <= 2;
题目 10
sql
SELECT
emp_name, dept_name, sale_amt,
MAX(sale_amt) OVER(PARTITION BY dept_name) - sale_amt AS 与最高差值,
sale_amt - MIN(sale_amt) OVER(PARTITION BY dept_name) AS 与最低差值
FROM sale_employee;
综合实战题答案
题目 11
sql
SELECT
dept_name, sale_month, 部门月销售额,
ROUND((部门月销售额 - 上月销售额)/上月销售额 * 100, 2) AS 环比增长率
FROM (
SELECT
dept_name, sale_month,
SUM(sale_amt) AS 部门月销售额,
LAG(SUM(sale_amt),1,NULL) OVER(PARTITION BY dept_name ORDER BY sale_month) AS 上月销售额
FROM sale_employee GROUP BY dept_name, sale_month
);
题目 12
sql
SELECT DISTINCT a.emp_name, a.sale_amt AS 一月销售额, b.sale_amt AS 二月销售额
FROM sale_employee a
JOIN sale_employee b ON a.emp_id = b.emp_id
WHERE a.sale_month = '2025-01' AND b.sale_month = '2025-02';
题目 13
sql
SELECT * FROM (
SELECT
emp_name, dept_name, sale_amt,
AVG(sale_amt) OVER(PARTITION BY dept_name) AS 部门平均值
FROM sale_employee
) WHERE sale_amt > 部门平均值;
四、知识点总结(必背)
- 排名函数
ROW_NUMBER():连续不重复RANK():并列 + 跳跃DENSE_RANK():并列 + 连续
- 聚合开窗 :
SUM/AVG/COUNT/MAX/MIN() OVER(PARTITION BY 分区字段 ORDER BY 排序) - 偏移函数 :
LAG(列, 偏移步长)取上一行,LEAD()取下一行 - 分组函数 :
NTILE(n)平均分成 n 组 - 核心语法 :
开窗函数 OVER(分区 排序 行范围)
五、文档使用说明
- 直接复制 创建表 SQL 到 Oracle 客户端(PL/SQL Developer/SQL Developer)执行
- 按难度顺序做题,先自行编写,再对照答案校验
- 可用于:个人练习、团队培训、面试题库