热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MySQL窗口函数详解与应用

本文介绍了MySQL窗口函数的基本概念、应用场景及常见函数的使用方法。窗口函数在处理复杂查询时非常有用,例如计算每个用户的订单排名、环比增长率、以及动态聚合等。
### 1. 应用场景

窗口函数适用于多种复杂的查询需求,例如:
- 如何计算患者上次就诊距今的时间?
- 如何计算环比增长率?
- 如何获取每个部门薪资排名前N的员工列表?
- 如何计算每个部门每人的薪资占部门总薪资的百分比?

对于这些需求,使用传统的SQL语句实现起来较为复杂,通常需要编写冗长的自连接SQL,不仅效率低下,而且难以维护。使用窗口函数可以显著简化这些查询。

### 2. 窗口函数简介

MySQL从8.0版本开始支持窗口函数,这一功能在大多数商业数据库和部分开源数据库中早已存在,有时也称为分析函数。

#### 什么是窗口?

窗口可以理解为一组记录的集合,窗口函数是在满足特定条件的记录集合上执行的特殊函数。对于每条记录,窗口函数都会在这个窗口内执行,有的函数窗口大小固定(静态窗口),有的函数窗口大小随记录变化(滑动窗口)。

#### 窗口函数与普通聚合函数的区别

- **聚合函数**:将多条记录聚合为一条记录。
- **窗口函数**:每条记录都会执行,最终结果仍然是多条记录。
- **聚合函数可以用于窗口函数**:例如,可以在窗口内计算动态的聚合值。

### 3. 窗口函数的基本用法

窗口函数的基本语法如下:

```sql
函数名([expr]) OVER (窗口子句)
```

其中,`OVER` 是关键字,用于指定函数执行的窗口范围。窗口子句可以包含以下部分:

- **窗口名称**:给窗口指定一个别名,使SQL更清晰易读。
- **PARTITION BY 子句**:按指定字段分组,窗口函数在不同的分组上分别执行。
- **ORDER BY 子句**:按指定字段排序,窗口函数按排序后的记录顺序执行。
- **FRAME 子句**:定义窗口的子集,通常用于滑动窗口。

### 4. 常见窗口函数应用

#### 4.1 序号函数

- **ROW_NUMBER()**:生成唯一序号。
- **RANK()**:生成带有并列序号的排名。
- **DENSE_RANK()**:生成不带空缺序号的排名。

##### 示例:查询每个用户订单金额最高的前三个订单

```sql
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY user_no ORDER BY amount DESC) AS row_num,
order_id, user_no, amount, create_date
FROM order_tb
) AS t
WHERE t.row_num <= 3;
```

#### 4.2 分布函数

- **PERCENT_RANK()**:计算当前行的百分位数。
- **CUME_DIST()**:计算当前行的累积分布。

##### 示例:计算订单金额的百分位数

```sql
SELECT t.* FROM (
SELECT RANK() OVER w AS Rank_num,
PERCENT_RANK() OVER w AS percent,
order_id, user_no, amount
FROM order_tb
WINDOW w AS (PARTITION BY user_no ORDER BY amount DESC)
) AS t;
```

#### 4.3 前后函数

- **LAG(字段名, n)**:获取当前行前n行的值。
- **LEAD(字段名, n)**:获取当前行后n行的值。

##### 示例:查询上一个订单距离当前订单的时间间隔

```sql
SELECT order_id, user_no, amount, create_date, last_date, DATEDIFF(create_date, last_date) AS diff
FROM (
SELECT order_id, user_no, amount, create_date,
LAG(create_date, 1) OVER w AS last_date
FROM order_tb
WINDOW w AS (PARTITION BY user_no ORDER BY create_date)
) AS t;
```

#### 4.4 头尾函数

- **FIRST_VALUE(expr)**:获取分区中的第一个值。
- **LAST_VALUE(expr)**:获取分区中的最后一个值。

##### 示例:查询截止到当前订单,按照日期排序的第一个订单和最后一个订单的订单金额

```sql
SELECT * FROM (
SELECT order_id, user_no, amount, create_date,
FIRST_VALUE(amount) OVER w AS first_amount,
LAST_VALUE(amount) OVER w AS last_amount
FROM order_tb
WINDOW w AS (PARTITION BY user_no ORDER BY create_date)
) AS t;
```

#### 4.5 其他函数

- **NTH_VALUE(expr, n)**:获取窗口中第n个值。
- **NTILE(n)**:将分区中的有序数据分为n个桶。

##### 示例:将每个用户的订单按照订单金额分成3组

```sql
SELECT * FROM (
SELECT NTILE(3) OVER w AS nf,
order_id, user_no, amount, create_date
FROM order_tb
WINDOW w AS (PARTITION BY user_no ORDER BY amount DESC)
) AS t;
```

#### 4.6 聚合函数作为窗口函数

聚合函数(如SUM、AVG、MAX、MIN、COUNT)可以在窗口中每条记录上动态应用,计算指定窗口内的聚合值。

##### 示例:计算每个用户截止到当前订单的累计订单金额、平均订单金额、最大订单金额、最小订单金额和订单数

```sql
SELECT * FROM (
SELECT order_id, user_no, amount, create_date,
SUM(amount) OVER w AS sum1,
AVG(amount) OVER w AS avg1,
MAX(amount) OVER w AS max1,
MIN(amount) OVER w AS min1,
COUNT(amount) OVER w AS count1
FROM order_tb
WINDOW w AS (PARTITION BY user_no ORDER BY order_id)
) AS t;
```

### 5. 总结

窗口函数在处理复杂查询时非常强大,可以显著简化SQL语句,提高查询效率。通过本文的介绍,希望读者能够更好地理解和应用窗口函数,提升SQL查询能力。
推荐阅读
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 20100423:Fixes:更新批处理,以兼容WIN7。第一次系统地玩QT,于是诞生了此预备式:【QT版本4.6.0&#x ... [详细]
  • 解析SQL查询结果的排序问题及其解决方案
    本文探讨了为什么某些SQL查询返回的数据集未能按预期顺序排列,并提供了详细的解决方案,帮助开发者理解并解决这一常见问题。 ... [详细]
  • 本文详细介绍了如何通过RPM包在Linux系统(如CentOS)上安装MySQL 5.6。涵盖了检查现有安装、下载和安装RPM包、配置MySQL以及设置远程访问和开机自启动等步骤。 ... [详细]
  • 本文探讨了使用C#在SQL Server和Access数据库中批量插入多条数据的性能差异。通过具体代码示例,详细分析了两种数据库的执行效率,并提供了优化建议。 ... [详细]
  • 本文详细介绍了网络存储技术的基本概念、分类及应用场景。通过分析直连式存储(DAS)、网络附加存储(NAS)和存储区域网络(SAN)的特点,帮助读者理解不同存储方式的优势与局限性。 ... [详细]
  • 本文详细介绍了 MySQL 数据库中的基础操作,包括创建、查询、修改和删除数据库、表及数据的命令。通过具体的 SQL 语句示例,帮助读者快速掌握 MySQL 的基本操作。 ... [详细]
  • 本文详细介绍了在 MySQL、SQL Server 和 Oracle 数据库中如何使用分组和排序功能。涵盖了聚集函数的应用、HAVING 子句的作用以及特定数据库中的独特方法,如 SQL Server 的 ROW_NUMBER() 函数和 Oracle 的相关特性。 ... [详细]
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • 本文介绍了一种在 MySQL 客户端执行 NOW() 函数时出现时间偏差的问题,并详细描述了如何通过配置文件调整时区设置来解决该问题。演示场景中,假设当前北京时间为2023年2月17日19:31:37,而查询结果显示的时间比实际时间晚8小时。 ... [详细]
  • This pull request introduces the ability to provide comprehensive paragraph configurations directly within the Create Note and Create Paragraph REST endpoints, reducing the need for additional configuration calls. ... [详细]
  • 本文介绍如何在SQL Server中创建动态SQL存储过程,并提供详细的代码实例和解释。通过这种方式,可以更灵活地处理查询条件和参数。 ... [详细]
  • 方法:1 配置数据库basediros.path.abspath(os.path.dirname(__file__))  #获取当前文件的绝对路径appFlask(__name__ ... [详细]
  • 反向投影技术主要用于在大型输入图像中定位特定的小型模板图像。通过直方图对比,它能够识别出最匹配的区域或点,从而确定模板图像在输入图像中的位置。 ... [详细]
  • 本文详细介绍了如何在 MySQL 中授予和撤销用户权限。包括创建用户、赋予不同级别的权限(如表级、数据库级、服务器级)、使权限生效、查看用户权限以及撤销权限的方法。此外,还提供了常见错误及其解决方法。 ... [详细]
author-avatar
neixi_0592045
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有