SQL 开窗函数(Window Function)是一种强大的分析工具,它能在保留原有数据行的基础上,对 "窗口"(指定范围的行集合)进行聚合、排名或分析计算,解决了传统GROUP BY聚合会合并行的局限性。

一、开窗函数的核心特点

  • 不合并行:与GROUP BY不同,开窗函数计算后会保留所有原始行,只是为每行附加一个计算结果。
  • 窗口定义:通过OVER()子句定义 "窗口"(即计算范围),可按条件分区、排序或限定行范围。
  • 适用场景:排名(如 top N)、累计计算(如累计求和)、移动分析(如近 3 天平均值)、前后行数据获取等。

二、基本语法结构

开窗函数的通用语法:

函数名(参数) OVER ([PARTITION BY 分区列1, 分区列2...]  -- 可选:按列分组,每组独立计算[ORDER BY 排序列1 [ASC|DESC], ...]  -- 可选:分区内的排序方式[ROWS | RANGE 窗口范围]  -- 可选:定义窗口的具体行范围(行级窗口)
)

  • 函数名:可以是排名函数(RANK()ROW_NUMBER()等)、聚合函数(SUM()AVG()等)或分析函数(LAG()LEAD()等)。
  • OVER()子句:核心部分,用于定义 "窗口" 的规则。

三、OVER()子句详解

1. PARTITION BY:分区(分组)
  • 作用:将数据按指定列分成多个独立的 "分区",开窗函数在每个分区内单独计算(类似GROUP BY的分组,但不合并行)。
  • 示例:按 "部门" 分区,每个部门内部独立计算工资排名。
2. ORDER BY:分区内排序
  • 作用:指定分区内的行排序规则,影响排名函数的结果和窗口范围的界定。
  • 注意:若不指定PARTITION BY,则全表视为一个分区,按ORDER BY整体排序。
3. ROWS | RANGE:窗口范围(行级窗口)
  • 作用:在分区内,进一步限定参与计算的行范围(如 "当前行 + 前 2 行 + 后 1 行")。
  • 关键字:
    • ROWS:基于物理行数界定范围(如 "前 2 行")。
    • RANGE:基于值的逻辑范围界定(如 "值在当前行 ±10 以内的行"),仅支持数值 / 日期类型。
  • 常用范围表达式:
    • UNBOUNDED PRECEDING:分区的第一行
    • CURRENT ROW:当前行
    • n PRECEDING:当前行之前的第 n 行
    • n FOLLOWING:当前行之后的第 n 行
    • 组合示例:ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING(当前行 + 前 2 行 + 后 1 行)

四、常用开窗函数分类及示例

以下示例基于员工表employee,结构如下:

idnamedepartmentsalaryhire_date
1张三技术部80002020-01-15
2李四技术部90002019-03-20
3王五技术部90002018-05-10
4赵六市场部70002021-02-05
5钱七市场部85002020-08-18
1. 排名函数(用于生成排名)
(1)ROW_NUMBER():生成唯一序号
  • 功能:为分区内的每行分配一个连续的唯一序号(即使值相同,序号也不同)。
  • 示例:按部门分区,按工资降序排名(工资相同则按入职时间升序):
SELECT name, department, salary,ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC, hire_date ASC) AS row_num
FROM employee;
  • 结果:
    namedepartmentsalaryrow_num
    李四技术部90001(同工资,入职早排前)
    王五技术部90002
    张三技术部80003
    钱七市场部85001
    赵六市场部70002
(2)RANK():带跳号的排名
  • 功能:相同值排名相同,后续排名会 "跳号"(如两个第 1 名,下一个是第 3 名)。
  • 示例:按部门分区,按工资降序排名:
SELECT name, department, salary,RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_num
FROM employee;

  • 结果:
    namedepartmentsalaryrank_num
    李四技术部90001
    王五技术部90001(与李四并列第 1)
    张三技术部80003(跳号,直接第 3)
    钱七市场部85001
    赵六市场部70002
(3)DENSE_RANK():无跳号的排名
  • 功能:相同值排名相同,后续排名不跳号(如两个第 1 名,下一个是第 2 名)。
  • 示例:按部门分区,按工资降序排名:
SELECT name, department, salary,DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank_num
FROM employee;

  • 结果:
    namedepartmentsalarydense_rank_num
    李四技术部90001
    王五技术部90001
    张三技术部80002(不跳号,第 2)
    钱七市场部85001
    赵六市场部70002
2. 聚合开窗函数(聚合函数 +OVER()

SUM()AVG()COUNT()等聚合函数与OVER()结合,为每行计算所在窗口的聚合结果。

(1)全分区聚合(无ORDER BY和范围)
  • 功能:计算整个分区的聚合值(每行的结果相同)。
  • 示例:计算每个部门的平均工资,附加到每行:
SELECT name, department, salary,AVG(salary) OVER (PARTITION BY department) AS dept_avg_salary
FROM employee;

  • 结果:
    namedepartmentsalarydept_avg_salary
    张三技术部80008666.67((8000+9000+9000)/3)
    李四技术部90008666.67
    王五技术部90008666.67
(2)累计聚合(带ORDER BY和范围)
  • 功能:按排序顺序计算 "累计" 聚合值(如累计求和、累计平均值)。
  • 示例:按部门分区,按入职时间升序,计算累计工资总和:
SELECT name, department, hire_date,salary,SUM(salary) OVER (PARTITION BY department ORDER BY hire_date ASCROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 从第一行到当前行) AS cumulative_salary
FROM employee;
  • 结果(技术部):
    namedepartmenthire_datesalarycumulative_salary
    王五技术部2018-05-1090009000(第一行,累计 = 自身)
    李四技术部2019-03-20900018000(累计 = 9000+9000)
    张三技术部2020-01-15800026000(累计 = 9000+9000+8000)
3. 分析函数(获取前后行数据)
(1)LAG(列名, n):获取当前行的前 n 行数据
  • 功能:返回当前行之前第 n 行的指定列值(默认 n=1)。
  • 示例:获取每个部门中,当前员工的前一位入职员工的工资:
SELECT name, department, hire_date,salary,LAG(salary, 1) OVER (PARTITION BY department ORDER BY hire_date ASC) AS prev_emp_salary
FROM employee;

  • 结果(技术部):
    namedepartmenthire_datesalaryprev_emp_salary
    王五技术部2018-05-109000NULL(第一行,无前一行)
    李四技术部2019-03-2090009000(前一行是王五的工资)
    张三技术部2020-01-1580009000(前一行是李四的工资)
(2)LEAD(列名, n):获取当前行的后 n 行数据
  • 功能:返回当前行之后第 n 行的指定列值(默认 n=1)。
  • 示例:获取每个部门中,当前员工的后一位入职员工的工资:
SELECT name, department, hire_date,salary,LEAD(salary, 1) OVER (PARTITION BY department ORDER BY hire_date ASC) AS next_emp_salary
FROM employee;

  • 结果(技术部):
    namedepartmenthire_datesalarynext_emp_salary
    王五技术部2018-05-1090009000(后一行是李四的工资)
    李四技术部2019-03-2090008000(后一行是张三的工资)
    张三技术部2020-01-158000NULL(最后一行,无后一行)

五、开窗函数与GROUP BY的区别

特性GROUP BY聚合开窗函数
行处理合并分组后的行(一行 / 组)保留所有原始行
计算范围整个分组可自定义窗口范围(分区、行范围)
结果列仅聚合结果 + 分组列原始列 + 开窗计算结果

六、注意事项

  1. 排序影响ORDER BY在开窗函数中不仅影响排名,还会影响窗口范围的界定(如累计计算)。
  2. 性能考量:复杂的窗口范围(如RANGE)可能导致性能下降,大表建议优先用ROWS
  3. 数据库支持:主流数据库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle)均支持开窗函数,但部分细节可能有差异。

通过上述讲解,可掌握开窗函数的核心语法和应用场景。实际使用时,需根据业务需求灵活组合PARTITION BYORDER BY和窗口范围,实现复杂的数据分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95285.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95285.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95285.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java静态代理和动态代理

Java静态代理和动态代理 静态代理 现在有一个计算类,有四个方法,加减乘除,如果需要给这四个方法都加上同一个逻辑,可以创建一个类作为代理类,把计算类注入到这个类中,然后再代理类中定义方法,并…

MySQL——MySQL引擎层BufferPool工作过程原理

目录一、MySQL引擎层BufferPool工作过程图解二、MySQL引擎层BufferPool工作过程原理一、MySQL引擎层BufferPool工作过程图解 图解 二、MySQL引擎层BufferPool工作过程原理 首先关闭自动提交,执行一条修改语句。 SET AUTOCOMMIT 0; update employees set name张三…

Python初学者笔记第二十二期 -- (JSON数据解析)

第31节课 JSON数据解析 1.JSON基础概念 JSON 是一种轻量级的数据交换格式(另一个叫XML),具有简洁、易读的特点,并且在不同编程语言之间能很好地实现数据传递。在 Python 中,json模块能够实现 Python 数据类型与 JSON 数…

基于多模态大模型的个性化学习路径生成系统研究

摘要 随着互联网技术的迅猛发展,个性化学习路径生成系统的研究在教育领域日益凸显其重要性。本研究聚焦于基于多模态大模型的个性化学习路径生成系统,旨在通过整合多模态数据,为学习者提供更加精准、个性化的学习路径。多模态大模型&#xf…

ESP32 烧录固件失败原因排除

ESP32 烧录固件时,有哪些特殊引脚需要注意电平状态的在 ESP32 烧录固件时,有几个关键引脚的电平状态会直接影响烧录过程,需要特别注意:GPIO0(BOOT 引脚):烧录模式:需要拉低&#xff…

3D视觉系统在机器人行业中的应用

视觉引导机器人技术(VGR)具有成熟的2D成像技术,但是经济高效的3D技术的出现使机器人应用的可能性更大。工业自动化的第一次迭代使用“盲”机器人,该机器人取决于待处理材料的精确定位。这样的机器人相对不灵活,只能通过…

MySQL高可用改造之数据库开发规范(大事务与数据一致性篇)

文章目录一、前言二、延迟的原因三、大事务处理规范3.1. 删除类操作优化设计3.2. 大事务通用拆分原则四、数据一致性核对规范4.1. 主从变更记录识别方法五、小结一、前言 MySQL 高可用架构中最基础、最为核心的内容:MySQL 复制(Replication)…

第9节 大模型分布式推理核心挑战与解决方案

文章目录 # 前言 一、通信瓶颈突破:让数据“跑”得更快 1. 问题:通信为什么会成为瓶颈? 2. 解决方案:从硬件到算法的全链路优化 (1)硬件层:升级“高速公路” (2)算法层:给数据“瘦身”并“错峰出行” (3)架构层:让数据“少跑路” 3. 效果评估:如何判断通信瓶颈已…

ESP32开发板接4阵脚屏幕教程(含介绍和针脚编号对应)

“4针屏幕” 一般有两种常见类型:IC 屏幕(如 0.96" OLED、SSD1306 等) 4 个针脚通常是:VCC → 接 ESP32 的 3.3V(有的屏幕支持 5V)GND → 接 ESP32 的 GNDSCL(时钟)→ 接 ESP32…

2025 年国内可用 Docker 镜像加速器地址

文章目录一、加速器地址搭建 Docker 镜像源二、使用一、加速器地址 docker.1ms.rundocker.domys.ccdocker.imgdb.dedocker-0.unsee.techdocker.hlmirror.comcjie.eu.orgdocker.m.daocloud.iohub.rat.devdocker.1panel.livedocker.rainbond.cc 搭建 Docker 镜像源 以上镜像源…

[Robotics_py] 路径规划算法 | 启发式函数 | A*算法

第五章:路径规划算法 欢迎回来,未来的机器人专家-=≡(・ω・) 在之前的章节中,我们已为机器人配备了核心知识:它能够跟踪自身的机器人状态/位姿,利用环境表示(栅格地图)理…

解决 HTTP 请求 RequestBody 只能被读取一次的问题

简介 HTTP 请求 RequestBody 只能被读取一次:HttpServletRequest 的输入流 (InputStream) 在被读取后会被关闭,导致后续无法再次读取。本文将介绍如何通过 请求包装类 (RequestWrapper) 来解决这个问题。问题背景 当我们需要在以下场景中多次读取 Reques…

(LeetCode 面试经典 150 题) 226. 翻转二叉树 (深度优先搜索dfs )

题目:226. 翻转二叉树 思路:深度优先搜索dfs,时间复杂度0(n)。 C版本: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr)…

2025牛客暑期多校训练营3(FDJAEHB)

题目链接&#xff1a;牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ F Flower 思路 可知当n<a时无论怎么操作她都会离开 n%(ab&#xff09;是指进行完若干轮之后剩下的不足ab个&#xff0c;如果是>a的话那么最后一轮必然不在a中&#xff0c;否则就…

【KO】 Android基础

以下是对这些 Android 相关问题的解答: 1. Activity 与 Fragment 之间常见的几种通信方式 接口回调:Fragment 定义接口,Activity 实现该接口,Fragment 通过接口实例调用方法传递数据 。 使用 Bundle:Fragment 可通过 setArguments(Bundle) 传数据给自身,Activity 可在创…

Gradle构建工具教程:由来与发展史(版本演进与未来优势)

一、Gradle简介Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具&#xff0c;使用基于Groovy的领域特定语言&#xff08;DSL&#xff09;声明项目设置。相较于传统XML配置&#xff0c;这种DSL使构建脚本更简洁易读。Gradle支持Java、Groovy、Kotlin、Sca…

@Rancher简介部署使用 - Docker Compose

Rancher 安装和使用介绍 - Docker Compose 文章目录Rancher 安装和使用介绍 - Docker Compose1. Rancher 简介1.1 什么是 Rancher1.2 Rancher 核心功能1.3 Rancher 架构2. 安装前准备2.1 系统要求2.2 环境准备3. 使用 Docker Compose 安装 Rancher3.1 创建 Docker Compose 文件…

程序员接私活的一些平台和建议,千万要注意,别掉坑里!

关于程序员接私活&#xff0c;社会各界说法不一&#xff0c;如果你确实急用钱&#xff0c;价格又合适&#xff0c;那就去做。 不过&#xff0c;私活也没有那么好做&#xff0c;一般私活的性价比远比上班拿工资的低。但是作为一个额外的收益渠道&#xff0c;一部分生活窘迫的程序…

多轮问答与指代消解

目录引言一、LangChain是怎么实现的多轮问答1、记忆模块&#xff08;Memory&#xff09;管理对话历史‌2、对话链&#xff08;Conversational Chain&#xff09;架构‌3、智能体&#xff08;Agent&#xff09;决策机制‌4、上下文感知的Prompt工程‌5、RAG&#xff08;检索增强…

文件IO、文件IO与标准IO的区别

一、文件IO --->fd&#xff08;文件描述符&#xff09;打开文件open读、写文件read/write关闭文件close#include <sys/types.h>#include <sys/stat.h>#include<fcntl.h>文件描述符&#xff1a;操作系统中已打开文件的标识符。小的、非负的整形数据范围&am…