数据库的聚合函数和窗口函数

news2025/1/9 16:26:23

1. 聚合函数

数据库的聚合函数是用于对数据集执行聚合计算的函数。它们将一组值作为输入,并生成单个聚合值作为输出。聚合函数通常与GROUP BY子句结合使用,以便在数据分组的基础上执行聚合操作。

1.1. 常用的聚合函数

  1. COUNT():计算指定列或表达式的行数。

  2. SUM():计算指定列或表达式的总和。

  3. AVG():计算指定列或表达式的平均值。

  4. MIN():找到指定列或表达式的最小值。

  5. MAX():找到指定列或表达式的最大值。

  6. GROUP_CONCAT()(MySQL)/ STRING_AGG()(PostgreSQL):将指定列的值连接成一个字符串,并可选择添加分隔符。

  7. STDDEV():计算指定列或表达式的标准差。

  8. VARIANCE():计算指定列或表达式的方差。

  9. FIRST():返回指定列或表达式的第一个非空值。

  10. LAST():返回指定列或表达式的最后一个非空值。

2. 窗口函数

数据库的窗口函数是一类强大的函数,它们用于在查询结果的特定窗口或分区上执行计算、排序、聚合等操作,而不影响查询结果集本身。窗口函数通常与OVER子句一起使用,以定义窗口的范围和排序规则。

2.1. 常用的窗口函数

  1. ROW_NUMBER():为结果集中的每一行分配一个唯一的行号。

  2. RANK():根据指定的排序规则,为结果集中的每一行分配一个排名,相同值的行将获得相同的排名,并跳过相应数量的排名。

  3. DENSE_RANK():类似于RANK()函数,但它不会跳过相同值的排名,而是按照顺序分配连续的排名。

  4. NTILE(n):将结果集分成 n 个相等大小的桶(窗口),并为每个桶分配一个标识符。

  5. LEAD(column, offset [, default]):获取当前行之后偏移量为 offset 的行的值。可以用于计算行与后续行之间的差值或趋势。

  6. LAG(column, offset [, default]):获取当前行之前偏移量为 offset 的行的值。可以用于计算行与前一行之间的差值或趋势。

  7. 聚合函数(例如SUM()、AVG()、MIN()、MAX())也可以用作窗口函数。它们可以与OVER子句结合使用,以在窗口范围内计算聚合值。

2.2. 窗口函数具体讲解

窗口函数跨一组与当前行有某种关联的表行执行计算。这与可以使用聚合函数完成的计算类型相当。但是,窗口函数不会像非窗口聚合调用那样将行分组为单个输出行。相反,他们保留了各自的身份。在后台,窗口函数能够访问的不仅仅是查询结果的当前行。

下面是一个例子,展示了如何比较每个员工的工资和他或她所在部门的平均工资:

SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;
depnameempnosalaryavg
develop1152005020.0000000000000000
develop742005020.0000000000000000
develop945005020.0000000000000000
develop860005020.0000000000000000
develop1052005020.0000000000000000
personnel535003700.0000000000000000
personnel239003700.0000000000000000
sales348004866.6666666666666667
sales150004866.6666666666666667
sales448004866.6666666666666667

前三个输出列直接来自表empsalary,表中的每一行对应一个输出行。第四列表示与当前行具有相同depname值的所有表行的平均值。(这实际上是与非窗口avg聚合相同的函数,但OVER子句导致它被视为窗口函数并跨窗口框架计算。)

窗口函数调用总是包含一个OVER子句,紧跟在窗口函数的名称和参数之后。这就是它在语法上区别于普通函数或非窗口聚合的地方。OVER子句准确地确定如何分割查询的行,以便由窗口函数进行处理。OVER中的PARTITION BY子句将行划分为组或分区,这些组或分区共享PARTITION BY表达式的相同值。对于每一行,窗口函数是在与当前行属于同一分区的行之间计算的。

您还可以使用OVER中的order by来控制窗口函数处理行的顺序。(窗口ORDER BY甚至不必匹配输出行的顺序。)下面是一个例子:

SELECT depname, empno, salary,
 rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;
depnameempnosalaryrank
develop860001
develop1052002
develop1152002
develop945004
develop742005
personnel239001
personnel535002
sales150001
sales448002
sales348002

如图所示,rank函数使用ORDER BY子句定义的顺序,为当前行的分区中的每个不同的ORDER BY值生成一个数字排名。rank不需要显式参数,因为它的行为完全由OVER子句决定。

窗口函数考虑的行是由查询的FROM子句过滤的WHERE、GROUP by和HAVING子句(如果有的话)生成的“虚拟表”。例如,由于不满足WHERE条件而删除的行不会被任何窗口函数看到。查询可以包含多个窗口函数,这些窗口函数使用不同的OVER子句以不同的方式分割数据,但它们都作用于这个虚拟表定义的同一行集合。

我们已经看到,如果行排序不重要,可以省略ORDER BY。也可以省略PARTITION BY,在这种情况下,只有一个包含所有行的分区。

与窗口函数相关的另一个重要概念是:对于每一行,在其分区内都有一组行,称为其窗框。一些窗函数只作用于窗框的行,而不是整个分区。默认情况下,如果提供了ORDER By,则框架由从分区开始到当前行的所有行组成,加上根据ORDER By子句与当前行相等的任何后续行。如果省略ORDER BY,则默认帧包含分区中的所有行。下面是一个使用sum的例子:

SELECT salary, sum(salary) OVER () FROM empsalary;
salarysum
520047100
500047100
350047100
480047100
390047100
420047100
450047100
480047100
600047100
520047100

上面,由于OVER子句中没有ORDER BY,因此窗口框架与分区相同,由于没有partition BY,分区就是整个表;换句话说,每个和都占用整个表,因此我们对每个输出行都得到相同的结果。但是如果我们添加一个ORDER BY子句,我们会得到非常不同的结果:

SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
salarysum
35003500
39007400
420011600
450016100
480025700
480025700
500030700
520041100
520041100
600047100

这里的总和是从第一个(最低)工资到当前工资,包括当前工资的任何重复(注意重复工资的结果)。

窗口函数只允许在查询的SELECT列表和ORDER BY子句中使用。它们在其他地方是被禁止的,比如GROUP BY、HAVING和WHERE子句。这是因为它们在逻辑上是在处理那些子句之后执行的。此外,窗口函数在非窗口聚合函数之后执行。这意味着在窗口函数的参数中包含聚合函数调用是有效的,反之则不然。

如果需要在执行窗口计算后对行进行筛选或分组,则可以使用子选择。例如:

SELECT depname, empno, salary, enroll_date
FROM
 (SELECT depname, empno, salary, enroll_date,
 rank() OVER (PARTITION BY depname ORDER BY salary DESC,
 empno) AS pos
 FROM empsalary
 ) AS ss
WHERE pos < 3;

上面的查询只显示内部查询中排名小于3的行。

当查询涉及多个窗口函数时,可以使用单独的OVER子句写出每个窗口函数,但是如果多个函数需要相同的窗口行为,那么这是重复的并且容易出错。相反,每个窗口行为都可以在WINDOW子句中命名,然后在OVER中引用。例如:

SELECT sum(salary) OVER w, avg(salary) OVER w
 FROM empsalary
 WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);

3. 数据库实例样式

3.1. 数据准备

DROP TABLE IF EXISTS "a_test_score";
CREATE TABLE "a_test_score" (
  "id" int4 NOT NULL DEFAULT nextval('a_test_score_id_seq'::regclass),
  "class_name" varchar(20) COLLATE "pg_catalog"."default",
  "stu_name" varchar(20) COLLATE "pg_catalog"."default",
  "year" int4,
  "score" numeric(255)
)
;
COMMENT ON COLUMN "a_test_score"."class_name" IS '班级名称';
COMMENT ON COLUMN "a_test_score"."stu_name" IS '用户名称';
COMMENT ON COLUMN "a_test_score"."year" IS '年份';
COMMENT ON COLUMN "a_test_score"."score" IS '成绩';

-- ----------------------------
-- Primary Key structure for table a_test_score
-- ----------------------------
ALTER TABLE "a_test_score" ADD CONSTRAINT "a_test_score_pkey" PRIMARY KEY ("id");

with

stu_info as (
	select 1 as userid,'高三1班' as class_name,'张三' as stu_name
	union all
	select 2 as userid,'高三1班' as class_name,'李四' as stu_name
	union all
	select 3 as userid,'高三1班' as class_name,'王五' as stu_name
	union all
	select 4 as userid,'高三2班' as class_name,'赵六' as stu_name
	union all
	select 5 as userid,'高三2班' as class_name,'周七' as stu_name
	union all
	select 6 as userid,'高三2班' as class_name,'吴八' as stu_name
	union all
	select 7 as userid,'高三3班' as class_name,'胡九' as stu_name
	union all
	select 8 as userid,'高三3班' as class_name,'钱十' as stu_name
	union all
	select 9 as userid,'高三3班' as class_name,'孙十一' as stu_name
	
),

year_info as (
	select 1 as id,2019 as year
	union all	
	select 1 as id,2020 as year
	union all	
	select 1 as id,2021 as year
	union all	
	select 1 as id,2022 as year
)

insert into a_test_score(class_name,stu_name,year,score)
select stu_info.class_name,stu_name,year_info.year,(random()*100)::integer from year_info cross join stu_info order by year,stu_info.userid

在这里插入图片描述

3.2. 聚合函数和窗口函数的区别

在这里插入图片描述

3.3. 窗口函数和聚合函数的使用

3.3.1. 聚合函数summinmax的使用

select max(year),min(year),sum(score) from a_test_score;

在这里插入图片描述

3.4. 窗口函数的使用

  1. sum作为窗口函数
  • 分组求和
select *,sum(score) over(PARTITION by year) from a_test_score where class_name='高三1班';

在这里插入图片描述

  • 分组求比例

select *,sum(score) over(PARTITION by year),score/(sum(score) over(PARTITION by year)) as score_bl_sum from a_test_score where class_name='高三1班';

在这里插入图片描述

以上写法同:

select t.*,(select sum(score) from a_test_score gt where gt.class_name=class_name and gt.year=year) from a_test_score t where class_name='高三1班';
  1. 故此可以实现以下表单汇总
  • 基础统计信息,包括每一条学生信息和他的汇总聚合信息
select *,
sum(score) over(PARTITION by year),score/(sum(score) over(PARTITION by year)) as score_bl_sum ,
max(score) over(PARTITION by class_name,year) as max_score,min(score) over(PARTITION by class_name,year) as min_score,count(score) over(PARTITION by class_name,year) as count_score
from a_test_score
order by "year",class_name;

在这里插入图片描述

  • 获取每个班级,每一个年度 分数最高的记录信息
select * from 
(
		SELECT id,class_name,stu_name, year, score,
           ROW_NUMBER() OVER (PARTITION BY class_name,year ORDER BY score DESC) AS rn
    FROM a_test_score
		order by "year",class_name
) t
where t.rn=1 

同,可以使用以下sql

select * from 
(
		SELECT id,class_name,stu_name, year, score,
           rank() OVER (PARTITION BY class_name,year ORDER BY score DESC) AS rn
    FROM a_test_score
		order by "year",class_name
) t
where t.rn=1 

在这里插入图片描述

  • RANKDENSE_RANK

RANK()DENSE_RANK() 是数据库中常用的窗口函数,用于为结果集中的每一行分配排名值。这两个函数通常在需要对结果集中的行进行排名时使用,它们都可以结合 ORDER BY 子句来指定排序规则。

RANK() 函数为结果集中的每一行分配排名值,具有相同值的行将获得相同的排名,并且可能跳过后续的排名。也就是说,如果有多行具有相同的排序键值(例如成绩),它们将被分配相同的排名,并且下一个排名将被跳过。

假设:以下数据

学生姓名成绩
Alice90
Bob85
Charlie92
David90
SELECT 学生姓名, 成绩, RANK() OVER (ORDER BY 成绩 DESC) AS 排名
FROM 表名;
学生姓名成绩排名
Charlie921
Alice902
David902
Bob854
DENSE_RANK() 函数也为结果集中的每一行分配排名值,但具有相同值的行将获得相同的排名,并且不会跳过后续的排名。也就是说,如果有多行具有相同的排序键值(例如成绩),它们将被分配相同的排名,并且下一个排名将紧接着。
SELECT 学生姓名, 成绩, DENSE_RANK() OVER (ORDER BY 成绩 DESC) AS 排名
FROM 表名;
学生姓名成绩排名
Charlie921
Alice902
David902
Bob853
  1. 其他窗口函数的使用

FIRST_VALUE()LAST_VALUE()LAG()LEAD()NTH_VALUE()

  • FIRST_VALUE() 函数

LAG(column, offset, default)函数返回当前行之前偏移量为 offset 的行的指定列的值。如果指定的偏移量超出了窗口范围,则可以提供 default 参数作为默认值。

  • LAST_VALUE() 函数

LAG(column, offset, default)函数返回当前行之前偏移量为 offset 的行的指定列的值。如果指定的偏移量超出了窗口范围,则可以提供 default 参数作为默认值。

  • LAG() 函数

LAG(column, offset, default)函数返回当前行之前偏移量为 offset 的行的指定列的值。如果指定的偏移量超出了窗口范围,则可以提供 default 参数作为默认值。

  • LEAD() 函数

LAG(column, offset, default)函数返回当前行之前偏移量为 offset 的行的指定列的值。如果指定的偏移量超出了窗口范围,则可以提供 default 参数作为默认值。

  • NTH_VALUE() 函数

NTH_VALUE(column, n)函数返回窗口内指定位置 n(从1开始)的行的指定列的值。

select *,
NTH_VALUE(score,2) over(PARTITION by class_name,year order by score desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as NTH_VALUE_score,
LEAD(stu_name,1,stu_name||'默认') over(PARTITION by class_name,year order by score desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as LEAD_VALUE_score,
FIRST_VALUE(stu_name) over(PARTITION by class_name,year order by score desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as FIRST_VALUE_score,
LAST_VALUE(stu_name) over(PARTITION by class_name,year order by score desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as LAST_VALUE_score
from a_test_score
order by "year",class_name;

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(五)springboot实战——springboot自定义事件的发布和订阅

前言 本节内容我们主要介绍一下springboot自定义事件的发布与订阅功能&#xff0c;一些特定应用场景下使用自定义事件发布功能&#xff0c;能大大降低我们代码的耦合性&#xff0c;使得我们应用程序的扩展更加方便。就本身而言&#xff0c;springboot的事件机制是通过观察者设…

Python(三十九)for-in循环

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

JAVA设计模式——模板设计模式(heima)

JAVA设计模式——模板设计模式&#xff08;heima&#xff09; 文章目录 JAVA设计模式——模板设计模式&#xff08;heima&#xff09;一、模板类二、子类2.1 Tom类2.2 Tony类 三、测试类 一、模板类 package _01模板设计模式;public abstract class TextTemplate{public final…

利用FME实现批量提取图斑特征点、关键界址点提取、图斑拐点抽稀,解决出界址点成果表时点数过多问题的方法

目录 一、实现效果 二、实现过程 1.提取图斑界址点 2.计算各界址点的角度 3.筛选提取关键界址点 三、总结 对于范围较大的图斑&#xff0c;界址点数目较大&#xff0c;在出界址点成果表前&#xff0c;往往需要对界址点进行处理&#xff0c;提取出关键特征点作为出界址点成…

数据库集群方案详解

本期直播我们邀请 KaiwuDB 资深解决方案专家周幸骏&#xff0c;为大家分享数据库集群方案详解。周老师毕业于复旦大学数学系&#xff0c;从业 20 余年&#xff0c;曾在 IBM 公司任资深技术专家&#xff0c;并为多家国有大型商业银行提供技术咨询和数据库业务连续方案设计等服务…

IBM:2023 年数据泄露的平均成本将达到 445 万美元

IBM 发布年度《数据泄露成本报告》&#xff0c;显示 2023 年全球数据泄露平均成本达到 445 万美元&#xff0c;比过去 3 年增加了 15%。创下该报告的历史新高。 报告显示&#xff0c;企业在计划如何应对日益增长的数据泄露频率和成本方面存在分歧。研究发现&#xff0c;虽然 95…

Linux学成之路(基础篇)(二十三)MySQL服务(上)

目录 一、概述 一、什么是MySQL 二、数据库能干什么 三、为什么要用数据库&#xff0c;优势、特性&#xff1f; 二、数据库类型 一、关系型数据库 RDBMS 一、概述 二、特点 三、代表产品 二、非关系型数据库 一、概述 二、特点 三、代表产品 三、数据库模型 一、…

【GEE笔记】主成分分析(PCA)算法的实现和应用

前言 主成分分析&#xff08;PCA&#xff09;是一种常用的降维方法&#xff0c;它可以将多个相关的变量转换为少数几个不相关的变量&#xff0c;称为主成分&#xff08;PC&#xff09;。这些主成分可以反映原始变量的大部分信息&#xff0c;同时减少数据的复杂度和冗余性。在遥…

结构型设计模式之组合模式【设计模式系列】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everythi…

【nginx】nginx中root与alias的区别:

文章目录 root与alias主要区别在于nginx如何解释location后面的uri&#xff0c;这会使两者分别以不同的方式将请求映射到服务器文件上。 root的处理结果是&#xff1a;root路径&#xff0b;location路径 alias的处理结果是&#xff1a;使用alias路径替换location路径 alias是一…

前端配置nginx反向代理

1.下载nginx 2.进入nginx.conf 3.配置 4.将nginx放入前端项目根目录 5.启动后端项目和nginx服务 启动命令 start nginx ; 重新启动命令 nginx -s reload 6.访问localhost就能看到项目了&#xff08;默认80端口&#xff09; 注意&#xff1a;如果nginx配置了域名&#xff0…

Android ConstraintLayout使用攻略

原文链接 Android ConstraintLayout使用攻略 ConstraintLayout是新一代的布局&#xff0c;它汲取了众家之长&#xff0c;把布局的概念进行了大统一&#xff0c;灵活且强大&#xff0c;基本上可以干掉以前所有的常用布局&#xff08;LinearLayout, RelativeLayout和FrameLayout…

94.qt qml-分页Table表格组件

在我们之前学习了87.qt qml-分页组件控件(支持设置任意折叠页数等)_qt分页控件_诺谦的博客-CSDN博客 然后我们又学习了Table实现,所以本章实现一个分页Table表格组件,配合分页控件, 模拟请求服务器数据来实现数据分解效果,因为一般使用分页的时候,一般都是分页请求,避免数…

Flutter的开发环境搭建-图解

前言&#xff1a;Flutter作为一个移动应用开发框架&#xff0c;具有许多优点和一些局限性。最大的优点就是-跨平台开发&#xff1a;Flutter可以在iOS和Android等多个平台上进行跨平台开发&#xff0c;使用一套代码编写应用程序&#xff0c;节省开发时间和成本。 Flutter可以编…

python将大文件拆分为多个小文件

如上图&#xff0c;目前采用单行不停写入的方式&#xff0c;这里是读了两次文件&#xff0c;第一次读取文件是为了获取总行数&#xff0c;第二次读取是取数据内容。 如果只读取一次文件&#xff0c;则会对内存有一定的要求&#xff0c;会需要在第一次读取数据的时候就将文件内…

ONNX Runtime 加速深度学习(C++ 、python)详细介绍

ONNX Runtime 加速深度学习(C 、python)详细介绍 本文在 https://blog.csdn.net/u013250861/article/details/127829944 基础上进行了更改&#xff0c;感谢原作&#xff01; ONNXRuntime(Open Neural Network Exchange)是微软推出的一款针对ONNX模型格式的推理框架&#xff0c…

Redis常用数据类型和使用场景

Redis目前支持5种数据类型&#xff0c;分别是&#xff1a; String&#xff08;字符串&#xff09; List&#xff08;列表&#xff09; Hash&#xff08;字典&#xff09; Set&#xff08;集合&#xff09; Sorted Set&#xff08;有序集合&#xff09; 下面就分别介绍这五…

Qt Core学习日记——第六天QMetaMethod

Qt子类会将每一个函数封装成QMetaMethod存储在对应的QMetaObject中&#xff0c;包括信号、槽函数、普通函数、构造函数、析构函数 函数解析 QMetaMethod::methodSignature 获取方法的签名 比如函数slot2&#xff0c;对应签名是“slot2(int*)” QMetaMethod::name 方法名称。…

13.2.3 【Linux】新增与移除群组

基本上&#xff0c;群组的内容都与这两个文件有关&#xff1a;/etc/group, /etc/gshadow。 群组的内容其实很简单&#xff0c;都是上面两个文件的新增、修改与移除而已。 groupadd 为了让使用者的 UID/GID 成对&#xff0c;建议新建的与使用者私有群组无关的其他群组时&#x…

RabbitMQ入门,springboot整合RabbitMQ

周末的两天没有写文章&#xff0c;因为项目分离出来了一个权限管理平台&#xff0c;花了一点时间整理项目&#xff0c;同时完成了一些功能的开发。 今天这篇文章介绍一下RabbitMQ这个消息中间件&#xff0c;以及通过springboot整合RabbitMQ。 目录 一、初步了解RabbitMQ 二、…