从数据汇总到高级分析,SQL 查询进阶实战(下篇)—— 分组、子查询与窗口函数全攻略

引言:从 "提取数据" 到 "洞察价值",SQL 进阶之路

在掌握了基础查询与多表关联后,你是否曾遇到这样的挑战:如何按部门统计平均薪资?怎样找出每个岗位薪资最高的员工?或者如何计算销售额的月度环比增长率?这些问题的核心,在于从 "简单提取数据" 升级为 "深度分析数据"

本文作为 SQL 查询系列的下篇,将聚焦分组查询、子查询、窗口函数 三大核心技术,带你突破数据处理的瓶颈。你将学习如何用GROUP BY实现 "按类别汇总",用子查询解决 "嵌套逻辑" 问题,更能掌握窗口函数的 "黑科技",轻松实现排名、累计值、移动平均等复杂分析。

目录

[引言:从 "提取数据" 到 "洞察价值",SQL 进阶之路](#引言:从 “提取数据” 到 “洞察价值”,SQL 进阶之路)

六、分组查询和聚合函数

一、统计函数(聚合函数)

特点:

[二、分组查询(GROUP BY)](#二、分组查询(GROUP BY))

[1. 基本语法](#1. 基本语法)

[2. 关键要点](#2. 关键要点)

[3. 示例解析](#3. 示例解析)

三、单行函数

四、统计函数与分组查询与单行函数的关系

三者同时使用

[七、WHERE 子查询与 FROM 子查询](#七、WHERE 子查询与 FROM 子查询)

[(一)、WHERE 子查询](#(一)、WHERE 子查询)

[1. 语法特点](#1. 语法特点)

[2. 示例解析](#2. 示例解析)

单行子查询(比较运算):

[多行子查询(IN 或 ANY):](#多行子查询(IN 或 ANY):)

聚合函数子查询:

[3. 关键字扩展(ANY/ALL)](#3. 关键字扩展(ANY/ALL))

[= ANY:](#= ANY:)

[< ANY:](#< ANY:)

[> ANY:](#> ANY:)

[> ALL:](#> ALL:)

[< ALL:](#< ALL:)

[(二)、FROM 子查询](#(二)、FROM 子查询)

[1. 语法特点](#1. 语法特点)

[2. 示例解析](#2. 示例解析)

简单子查询作为临时表:

预先过滤数据:

[3. 优势](#3. 优势)

[(三)、WHERE 子查询 vs FROM 子查询](#(三)、WHERE 子查询 vs FROM 子查询)

四、总结

八、SELECT查询

[(一)、SELECT 查询的基础用法](#(一)、SELECT 查询的基础用法)

字段选择规则

字段别名设置

函数与表达式支持

SELECT子查询

一、语法特点

二、示例解析

三、使用场景

四、注意事项

九、分页查询

(一)、分页查询的必要性

[(二)、分页查询语法(以 MySQL 为例)](#(二)、分页查询语法(以 MySQL 为例))

语法规则

示例

[1. 查询首页数据(前 10 条):](#1. 查询首页数据(前 10 条):)

[2. 查询第二页数据(第 11-20 条):](#2. 查询第二页数据(第 11-20 条):)

(三)、分页查询与其他语法的结合

与排序结合:

与筛选条件结合:

与连接查询结合:

(四)、分页查询的注意事项

十、函数整理

窗口函数

为什么窗口函数对数据分析岗位至关重要?

功能:

[1. 基础语法框架](#1. 基础语法框架)

[2. 常用窗口函数类型与示例](#2. 常用窗口函数类型与示例)

1)排名函数

[2)聚合函数 + 窗口函数](#2)聚合函数 + 窗口函数)

3)取值函数

[3. 窗口帧控制(ROWS vs RANGE)](#3. 窗口帧控制(ROWS vs RANGE))

[4. 核心特点总结](#4. 核心特点总结)


六、分组查询和聚合函数

一、统计函数(聚合函数)

统计函数用于对数据表中的数据进行汇总计算 ,返回单一结果。常见统计函数包括:

COUNT(字段):统计非NULL值的数量(如统计人数、记录数)。

  • 示例:

    SELECT COUNT(EMPNO) 人数 FROM emp;(统计员工总数)

AVG(字段):计算数值型字段的平均值(如平均工资)。

  • 示例:

    SELECT AVG(SAL) 平均工资 FROM emp;(计算所有员工平均工资)。

其他常用函数:SUM(字段)(求和)、MAX(字段)(最大值)、MIN(字段)(最小值)等。

特点

  • 可直接作用于单表,无需分组时,返回整个表的汇总结果。
  • 常与分组查询结合,对每个分组单独计算统计结果。

二、分组查询(GROUP BY)

分组查询用于将表中的数据按指定字段分组 ,对每个分组单独应用统计函数,实现 "按类别汇总" 的需求。

1. 基本语法

复制代码
SELECT 分组字段1, 分组字段2, 统计函数(字段)
FROM 表名
[WHERE 条件]  -- 分组前筛选原始数据(不涉及统计结果)
GROUP BY 分组字段1, 分组字段2  -- 按字段分组,字段需出现在SELECT中
[HAVING 统计条件]  -- 分组后筛选统计结果(需使用统计函数)
[ORDER BY 排序字段];  -- 对最终结果排序

2. 关键要点

WHERE vs HAVING

  • WHERE:在分组之前 筛选数据,不能使用统计函数(如WHERE AVG(SAL) > 2000 错误)。

  • HAVING:在分组之后 筛选统计结果,可使用统计函数(如HAVING AVG(SAL) > 2000 正确)。

分组字段 :GROUP BY 后的字段必须在 SELECT 中出现(除非使用统计函数覆盖),确保每个分组的唯一性。

3. 示例解析

按岗位分组统计人数:

复制代码
SELECT JOB, COUNT(EMPNO) 人数 FROM emp GROUP BY JOB;

# 4.查询每个岗位的详细信息,包含平均薪资
-- 查询每个岗位的详细信息,包含平均薪资
SELECT 
    emp.job 岗位,
    COUNT(emp.empno) 员工人数,
    AVG(emp.sal) 平均薪资,
    MIN(emp.sal) 最低薪资,
    MAX(emp.sal) 最高薪资,
    SUM(emp.sal) 薪资总和
FROM 
    emp
GROUP BY 
    emp.job
ORDER BY 
    平均薪资 DESC;

# 5.查询每个部门的详细信息,包含平均薪资
SELECT 
    d.deptno 部门编号,      -- 部门编号(来自 dept 表)
    d.dname 部门名称,      -- 部门名称(来自 dept 表)
    COUNT(e.empno) 部门人数,
    AVG(e.sal) 平均薪资
FROM 
    emp e
LEFT JOIN 
    dept d 
ON e.deptno = d.deptno  -- 连接员工表和部门表
GROUP BY 
    d.deptno;  -- 按部门分组(确保部门编号和名称唯一)

结合右连接查询,按部门分组并筛选平均薪资高于 2000 的部门:

复制代码
# 6.#查询平均薪资高于2000的部门信息
SELECT 
    d.deptno 部门编号,      -- 部门编号(来自 dept 表)
    d.dname 部门名称,      -- 部门名称(来自 dept 表)
    COUNT(e.empno) 部门人数,
    AVG(e.sal) 平均薪资
FROM 
    emp e
RIGHT JOIN 
    dept d
USING
		(DEPTNO) -- 连接员工表和部门表
GROUP BY 
    d.deptno  -- 按部门分组(确保部门编号和名称唯一)
HAVING
    avg(e.SAL) > 2000
ORDER BY  平均薪资 DESC;

三、单行函数

单行函数是对表中的每一行数据单独处理,返回与原表行数相同的结果。常见类型包括:

  • 字符函数 :UPPER(字段)(转大写)、LOWER(字段)(转小写)、LENGTH(字段)(长度)等。

    • 示例:SELECT UPPER(ENAME) FROM emp;(将员工姓名转为大写)。
  • 数值函数 :ROUND(字段, 小数位)(四舍五入)、TRUNC(字段, 小数位)(截断)等。

    • 示例:SELECT ROUND(SAL, 2) FROM emp;(工资保留两位小数)。
  • 日期函数 :SYSDATE()(当前系统时间)、TO_DATE(字符串, 格式)(字符串转日期)等。

    • 示例:SELECT TO_DATE('2025-01-01', 'YYYY-MM-DD') FROM dual;(将字符串转为日期)。
  • 转换函数 :TO_CHAR(字段, 格式)(日期 / 数值转字符串)、TO_NUMBER(字符串)(字符串转数值)等。

    • 示例:SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD') FROM dual;(当前日期转为字符串)。

特点

  • 处理粒度为 "单行",不改变表的行数,仅对每行数据进行转换或计算。

  • 可与统计函数、分组查询混合使用,实现更复杂的业务逻辑。

    SELECT
    deptno 部门编号,
    UPPER(ename) 部门名称大写, -- 单行函数:部门名称转大写
    COUNT(empno) 部门人数, -- 统计函数:按部门统计人数
    AVG(sal) 平均薪资 -- 统计函数:按部门计算平均工资
    FROM emp
    GROUP BY deptno, ename; -- 分组查询:按部门编号和名称分组

    具体来说,当你使用 GROUP BY 进行分组查询时,SELECT 后面只能出现两种类型的列:
    出现在 GROUP BY 子句中的列
    使用聚合函数(如 SUM(), COUNT(), AVG(), MAX(), MIN() 等)处理过的列

四、统计函数与分组查询与单行函数的关系

统计函数 vs 单行函数

  • 统计函数:对一组数据(可能是全表或分组)汇总计算,返回单一结果。

  • 单行函数:对每行数据单独处理,返回与原表行数相同的结果。

分组查询的桥梁作用

  • 分组查询通过 GROUP BY 将数据划分为多个子集,每个子集可应用统计函数生成汇总结果。

  • 单行函数可在分组前对原始数据进行转换(如日期格式化、字符串处理),或在分组后对统计结果进行二次处理。

例如:

  • 不分组:AVG(SAL) 计算所有员工的平均工资。

    所有人数

    SELECT COUNT(EMPNO) 人数
    FROM emp;

    所有员工平均工资

    SELECT AVG(SAL) 平均工资
    FROM emp;

  • 按部门分组:GROUP BY deptno + AVG(SAL) 计算每个部门的平均工资。

    统计各岗位平均工资

    SELECT JOB 岗位,AVG(SAL) 平均工资
    FROM emp
    GROUP BY JOB;

三者同时使用

复制代码
SELECT 
    deptno 部门编号,
    UPPER(dname) 部门名称大写,  -- 单行函数:部门名称转大写
    COUNT(empno) 部门人数,      -- 统计函数:按部门统计人数
    AVG(sal) 平均薪资           -- 统计函数:按部门计算平均工资
FROM emp
GROUP BY deptno, dname;  -- 分组查询:按部门编号和名称分组

SELECT 
    deptno 部门编号,
    UPPER(ename) 部门名称大写,  -- 单行函数:部门名称转大写
    COUNT(empno) 部门人数,      -- 统计函数:按部门统计人数
    AVG(sal) 平均薪资           -- 统计函数:按部门计算平均工资
FROM emp
GROUP BY deptno, ename;  -- 分组查询:按部门编号和名称分组
 
具体来说,当你使用 GROUP BY 进行分组查询时,SELECT 后面只能出现两种类型的列:
出现在 GROUP BY 子句中的列
使用聚合函数(如 SUM(), COUNT(), AVG(), MAX(), MIN() 等)处理过的列

七、WHERE 子查询与 FROM 子查询

(一)、WHERE 子查询

定义 :子查询出现在 WHERE 或 HAVING 子句中,作为条件表达式的一部分,用于筛选主查询的数据。

1. 语法特点

子查询结果需能作为条件值条件列表,常见形式:

  • 单行子查询:返回单一值,用于 =、>、< 等比较运算。

  • 多行子查询:返回多个值,用于 IN、ANY、ALL 等逻辑运算。

执行顺序:先执行子查询,再将结果作为条件筛选主查询数据。

2. 示例解析

单行子查询(比较运算)
复制代码
-- 查询比SMITH工资高的员工信息
SELECT *
FROM emp
WHERE SAL > (SELECT SAL FROM emp WHERE ENAME = 'SMITH')
ORDER BY SAL DESC;

子查询 (SELECT SAL FROM emp WHERE ENAME = 'SMITH') 返回 SMITH 的工资(单行值)。

主查询筛选工资大于该值的员工。

多行子查询(IN 或 ANY):
复制代码
-- 查询与销售岗位薪资相同的员工信息
SELECT *
FROM emp
WHERE SAL IN (SELECT SAL FROM emp WHERE JOB = 'SALESMAN');

子查询返回所有销售岗位的薪资(多行值)。

主查询筛选薪资在该列表中的员工。

聚合函数子查询:
复制代码
-- 查询比平均工资高的员工信息
SELECT *
FROM emp
WHERE SAL > (SELECT AVG(SAL) FROM emp);

3. 关键字扩展(ANY/ALL)

= ANY:

等价于 IN,匹配列表中任意一个值。

复制代码
SELECT *
FROM emp
WHERE SAL = ANY (SELECT SAL FROM emp WHERE JOB = 'SALESMAN');
< ANY:

小于列表中的最大值。

复制代码
SELECT *
FROM emp
WHERE SAL < ANY (SELECT SAL FROM emp WHERE JOB = 'SALESMAN');
> ANY:

比最小值大的数据

> ALL:

大于列表中的最大值。

< ALL:

小于列表中的最小值。

(二)、FROM 子查询

定义 :子查询出现在 FROM 子句中,作为主查询的 "临时表",需为子查询指定别名。

1. 语法特点

  • 子查询结果作为数据源,主查询对其进行二次筛选或关联。

  • 子查询可预先过滤数据,减少主查询的处理量。

  • 执行顺序:先执行子查询生成临时表,再执行主查询。

2. 示例解析

简单子查询作为临时表
复制代码
-- 与WHERE子查询示例1结果相同,但语法结构不同
SELECT *
FROM (
    SELECT *
    FROM emp
    WHERE SAL > (SELECT SAL FROM emp WHERE ENAME = 'SMITH')
) AS high_sal_emp  -- 必须指定别名
ORDER BY SAL DESC;

子查询生成 "工资高于 SMITH 的员工" 临时表。

主查询对临时表按工资降序排序。

预先过滤数据:
复制代码
SELECT *
FROM (
    SELECT empno, ename, sal, deptno
    FROM emp
    WHERE job = 'SALESMAN'
) AS salesmen
WHERE sal >= 1500;

子查询预先筛选出销售岗位的员工。

主查询在临时表基础上,进一步筛选薪资≥1500 的员工。

3. 优势

逻辑分层:将复杂查询拆分为 "数据准备层"(子查询)和 "结果处理层"(主查询),便于维护。

性能优化:子查询可提前过滤冗余数据,减少主查询的计算量。

(三)、WHERE 子查询 vs FROM 子查询

|-------|-------------------------------|----------------------------|
| 维度 | WHERE 子查询 | FROM 子查询 |
| 位置 | 出现在WHERE/HAVING子句中 | 出现在FROM子句中 |
| 作用 | 作为条件筛选主查询数据 | 作为数据源(临时表)供主查询使用 |
| 执行顺序 | 先执行子查询,再执行主查询 | 先执行子查询生成临时表,再执行主查询 |
| 子查询结果 | 需为单一值或值列表(匹配条件) | 需为完整数据集(可含多字段、多行) |
| 别名要求 | 无需别名 | 必须为子查询指定别名(临时表名) |
| 典型场景 | 基于子查询结果的条件筛选(如 "比 SMITH 工资高") | 复杂数据预处理(如 "先筛选销售岗位,再处理薪资") |

四、总结

WHERE 子查询:适用于将子查询结果作为条件,直接筛选主查询数据,语法简洁,适合简单条件判断。

FROM 子查询:适用于复杂数据预处理,通过临时表分层处理逻辑,便于维护和性能优化。

两者可结合使用,实现更复杂的业务需求(如子查询嵌套、多表关联等)。

八、SELECT查询

定义 :SELECT 是 SQL 中用于从表中提取数据的基础关键字,所有查询操作均以 SELECT 开头,决定返回哪些字段及如何展示。

(一)、SELECT 查询的基础用法

字段选择规则

  • 可选择表中所有字段 :用 * 表示(如 SELECT * FROM emp;),适合快速查看全表数据,但效率较低(不推荐大数据量场景)。

  • 可选择指定字段 :用逗号分隔字段名(如 SELECT EMPNO, ENAME, SAL FROM emp;),仅返回所需数据,减少资源消耗。

  • 支持字段去重 :在字段前加 DISTINCT(如 SELECT DISTINCT JOB FROM emp;),仅保留该字段的唯一值(DISTINCT 仅对紧跟的第一个字段生效)。

字段别名设置

  • 为字段指定可读性更强的别名(尤其适合中文展示),语法:字段名 别名 或 字段名 AS 别名(AS 可省略)。

  • 示例:SELECT ENAME 姓名, JOB 职位, SAL 薪资 FROM emp;

函数与表达式支持

可在 SELECT 后直接使用单行函数处理字段(如字符转换、数值计算等):

复制代码
SELECT UPPER(ENAME) 姓名大写, ROUND(SAL, 2) 薪资保留两位小数 FROM emp;

可在 SELECT 后使用统计函数(聚合函数)进行汇总计算(常与 GROUP BY 结合):

复制代码
SELECT JOB, COUNT(EMPNO) 人数, AVG(SAL) 平均薪资 FROM emp GROUP BY JOB;

可嵌套子查询作为字段值,返回与主查询行数一致的结果

复制代码
SELECT ENAME, SAL, (SELECT MAX(SAL) FROM emp) 最高工资 FROM emp;

SELECT子查询

定义 :SELECT子查询是嵌套在SELECT子句中的子查询,作为主查询返回结果的一个字段值,其结果与主查询的行数保持一致(每行返回一个对应值)。

一、语法特点

  1. 位置 :出现在SELECT子句中,作为一个 "虚拟字段" 存在。

  2. 结果要求 :子查询需返回单行单列的值(即单个数值或字符串),确保与主查询的每行记录一一对应。

  3. 执行逻辑:主查询每读取一行数据,子查询会执行一次,返回对应的值作为该字段的结果。

二、示例解析

复制代码
SELECT 
  ENAME 姓名,
  SAL 薪资,
  (SELECT MAX(SAL) FROM emp) AS 最高工资  -- SELECT子查询:返回全表最高工资
FROM emp;
  • 子查询(SELECT MAX(SAL) FROM emp)独立计算全表最高工资(单行单列结果)。

  • 主查询返回每位员工的姓名、薪资,同时将子查询结果作为 "最高工资" 字段,与每行员工信息对应。

三、使用场景

  1. 关联单行数据:获取与主查询每行记录相关的单行参考值(如对比个人薪资与全表最高薪资)。

  2. 简化多步查询 :无需单独执行子查询再手动关联结果,直接在SELECT中嵌套实现。

四、注意事项

  1. 子查询必须返回单行单列 结果,否则会报错(如返回多行时需配合LIMIT 1等限制)。

  2. 避免在大数据量场景中过度使用,因每行执行一次子查询可能导致性能下降(可通过关联查询优化)。

总结 :SELECT子查询通过在字段列表中嵌套单行子查询,为每条主查询记录附加一个动态计算的参考值,适用于需对比或补充单行关联数据的场景。

九、分页查询

(一)、分页查询的必要性

  • 性能优化:当表数据量庞大时(如百万级记录),一次性查询所有数据会导致:

  • 数据库查询耗时久,占用大量内存。

  • 网络传输数据量大,前端渲染压力大。分页查询可按需获取数据,显著提升效率。

  • 用户体验:网页或应用中,分页展示数据更符合用户浏览习惯(如 "下一页""上一页" 按钮),避免信息过载。

(二)、分页查询语法(以 MySQL 为例)

关键字 **:**LIMIT n, m

  • n:起始数据的索引(从 0 开始)。

  • m:每页显示的条数

语法规则

复制代码
SELECT 字段 FROM 表名
[WHERE 条件]
[ORDER BY 排序字段]
LIMIT n, m;

示例

1. 查询首页数据(前 10 条):
复制代码
SELECT * FROM emp LIMIT 0, 10;  -- 或简写为 LIMIT 10
  • n=0 表示从第一条数据开始(索引 0)。
  • m=10 表示每页显示 10 条。
2. 查询第二页数据(第 11-20 条):
复制代码
SELECT * FROM emp LIMIT 10, 10;
  • n=10 表示从第 11 条数据开始(索引 10)。
  • m=10 表示每页显示 10 条。

(三)、分页查询与其他语法的结合

与排序结合:

分页前需对数据排序,确保每页数据的顺序一致。

复制代码
SELECT * FROM emp
ORDER BY SAL DESC  -- 按工资降序排序
LIMIT 10, 10;      -- 查询第二页数据

与筛选条件结合:

先筛选数据,再分页展示。

复制代码
SELECT * FROM emp
WHERE DEPTNO = 10  -- 仅查询部门10的员工
LIMIT 0, 5;        -- 每页显示5条

与连接查询结合:

多表关联后分页展示结果。

复制代码
SELECT 
    e.ENAME, d.DNAME
FROM 
    emp e 
LEFT JOIN dept d 
ON e.DEPTNO = d.DEPTNO
LIMIT 0, 5;

(四)、分页查询的注意事项

  1. 数据库兼容性
  • MySQL 使用 LIMIT n, m。

  • Oracle 使用 ROWNUM 或 OFFSET...FETCH(如 OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY)。

  • SQL Server 使用 OFFSET...FETCH(与 Oracle 类似)。

  1. 索引优化:分页查询时,若涉及排序字段,需确保该字段有索引,否则大量数据排序会导致性能下降。

  2. 动态计算页码:在实际应用中,页码需根据总记录数和每页条数动态计算,例如:

  • 总记录数:SELECT COUNT(*) FROM emp;

  • 总页数:CEIL(总记录数 / 每页条数)

    SELECT COUNT() FROM emp;
    SELECT CEIL((SELECT COUNT(
    ) FROM emp) / 10);

十、函数整理

窗口函数

窗口函数是现代数据分析中处理复杂计算、排名、移动平均、累计值、分区内比较等任务的利器,是数据分析师日常工作中频繁使用的工具。

为什么窗口函数对数据分析岗位至关重要?

  1. 处理复杂排名和分位数: 计算销售排名、部门内薪资排名、成绩百分位等(ROW_NUMBER(), RANK(), DENSE_RANK(), NTILE(), PERCENT_RANK(), CUME_DIST())。

  2. 计算趋势和移动指标: 计算滚动平均销售额、移动总和、环比/同比变化等(SUM() OVER(...), AVG() OVER(...), 结合 ROWS/RANGE 子句)。

  3. 访问前后行数据: 分析用户行为序列(如上一步/下一步操作)、计算与前一行的差值或比率(LAG(), LEAD())。

  4. 分区内聚合而不折叠行: 计算每个员工薪资占其部门总薪资的比例、计算每个客户订单总额的同时保留订单明细(SUM() OVER(PARTITION BY ...))。

  5. 累积计算: 计算年初至今(YTD)销售额、累计用户数等(SUM() OVER(ORDER BY ... ROWS UNBOUNDED PRECEDING))。

  6. 高效处理"Top N per Group"问题: 找出每个部门薪资最高的前3名员工(结合 ROW_NUMBER() 或 RANK() 和子查询/CTE)。

功能:

对数据的指定分区(窗口) 进行跨行计算,不折叠结果集(保留原始行),实现排名、累积、移动平均等复杂分析。

与 GROUP BY 关键区别:

|------|-----------------|-----------------|
| 维度 | GROUP BY (分组聚合) | 窗口函数 |
| 结果行数 | 折叠为分组数(行数减少) | 保留原表行数(行数不变) |
| 计算粒度 | 每组返回一个汇总值 | 每行返回基于窗口的独立计算结果 |
| 典型场景 | 部门平均工资、岗位人数统计 | 部门内薪资排名、累计销售额分析 |

1. 基础语法框架

复制代码
SELECT  
    字段1,  
    字段2,  
    窗口函数() OVER (  
        [PARTITION BY 分区字段]  -- 将数据划分为多个窗口(类似分组)  
        [ORDER BY 排序字段]      -- 窗口内数据排序(影响排名、累积计算)  
        [ROWS/RANGE 窗口帧]      -- 定义计算范围(如 "当前行前3行")  
    ) AS 别名  
FROM 表名;  

2. 常用窗口函数类型与示例

1)排名函数

|--------------|------------------|-----------------|
| 函数 | 功能说明 | 示例场景 |
| ROW_NUMBER() | 为分区内每行生成唯一连续序号 | 按部门给员工薪资排名(无并列) |
| RANK() | 并列时跳过后续序号(1,1,3) | 销售业绩排名(允许名次并列) |
| DENSE_RANK() | 并列时不跳号(1,1,2) | 学生成绩等级排名(紧密排序) |

示例:查询每个部门内员工的薪资排名(允许并列)

复制代码
SELECT  
    DEPTNO 部门编号,  
    ENAME 员工姓名,  
    SAL 薪资,  
    RANK() OVER (  
        PARTITION BY DEPTNO    -- 按部门分区  
        ORDER BY SAL DESC      -- 薪资降序排序  
    ) AS 部门内薪资排名  
FROM emp;  

结果示例:

|------|-------|------|---------|---------------|
| 部门编号 | 员工姓名 | 薪资 | 部门内薪资排名 | |
| 10 | KING | 5000 | 1 | |
| 10 | CLARK | 2450 | 2 | |
| 20 | SCOTT | 3000 | 1 | |
| 20 | FORD | 3000 | 1 | -- 并列第一 |
| 20 | ADAMS | 1100 | 3 | -- RANK() 跳过2 |

2)聚合函数 + 窗口函数

功能:在分区内计算聚合值(如累加、移动平均),不折叠行。

常用函数:

复制代码
SUM(字段) OVER (...)  -- 累计求和  
AVG(字段) OVER (...)  -- 移动平均  
MAX/MIN(字段) OVER (...)  

示例1:计算每位员工的累计薪资(按入职顺序累加)

复制代码
SELECT  
    ENAME,  
    HIREDATE,  
    SAL,  
    SUM(SAL) OVER (  
        ORDER BY HIREDATE   -- 按入职日期排序  
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 从首行到当前行  
    ) AS 累计薪资  
FROM emp;  
3)取值函数

|-----------------|-------------|
| 函数 | 功能说明 |
| LAG(字段, N) | 获取当前行前N行的数据 |
| LEAD(字段, N) | 获取当前行后N行的数据 |
| FIRST_VALUE(字段) | 返回窗口内第一行的值 |
| LAST_VALUE(字段) | 返回窗口内最后一行的值 |

示例:分析每月销售额环比增长率

复制代码
SELECT  
    sale_month,  
    sales,  
    LAG(sales, 1) OVER (ORDER BY sale_month) AS 上月销售额,  
    (sales - LAG(sales, 1) OVER (ORDER BY sale_month)) / LAG(sales, 1) OVER (ORDER BY sale_month) AS 环比增长率  
FROM sales_table;  

3. 窗口帧控制(ROWS vs RANGE)

|-------|--------------|---------------------------------------------------------|
| 关键字 | 说明 | 示例 |
| ROWS | 物理行 范围(推荐使用) | ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING (当前行前后各1行) |
| RANGE | 逻辑值 范围(易混淆) | RANGE BETWEEN 100 PRECEDING AND 200 FOLLOWING (值在±100内) |

常用帧范围:

复制代码
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 从分区开始到当前行(累计算)  
ROWS BETWEEN 3 PRECEDING AND 1 FOLLOWING         -- 前3行到后1行  
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -- 当前行到分区结束  

4. 核心特点总结

  1. 不折叠结果:保留原表所有行,新增计算列,适合明细级分析报告。
  2. 分层计算:通过 PARTITION BY 实现"组内分析"(如部门内排名)。
  3. 动态范围:ORDER BY + 窗口帧 支持时间序列分析(移动平均、累计值)。
  4. 性能优化:比自连接/子查询更高效处理复杂分析(如 Top N 问题)。
  5. 面试重点:数据分析岗位必考,用于解决:
  • 排名问题(ROW_NUMBER(), RANK())
  • 趋势分析(LAG()/LEAD(), 移动平均)
  • 占比计算(SUM() OVER(PARTITION BY))
相关推荐
不羁。。28 分钟前
【撸靶笔记】第七关:GET - Dump into outfile - String
数据库·笔记·oracle
yangchanghua1112 小时前
pgsql 如何查询今天范围内的数据(当天0点0分0秒 - 当天23点59分59秒....)
数据库·pgsql
larance2 小时前
SQLAlchemy 的异步操作来批量保存对象列表
数据库·python
在努力的前端小白2 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
未来之窗软件服务2 小时前
自建知识库,向量数据库 (九)之 量化前奏分词服务——仙盟创梦IDE
数据库·仙盟创梦ide·东方仙盟·自建ai·ai分词
冒泡的肥皂6 小时前
MVCC初学demo(一
数据库·后端·mysql
.Shu.7 小时前
Redis Reactor 模型详解【基本架构、事件循环机制、结合源码详细追踪读写请求从客户端连接到命令执行的完整流程】
数据库·redis·架构
yatingliu20198 小时前
HiveQL | 个人学习笔记
hive·笔记·sql·学习
Bruce_Liuxiaowei9 小时前
MySQL完整重置密码流程(针对 macOS)
mysql