PostgreSQL 的窗口函数 OVER, WINDOW, PARTITION BY, RANGE
最近在数据处理中用到了窗函数, 把使用方法记录一下, 暂时只有分组排序和滑动时间窗口的例子, 以后再逐步添加
场景
在SQL查询时, 会遇到有两类需要分组统计的场景, 在之前的SQL语法中是不方便实现的
- 场景1: 顾客维修设备的记录表, 每次维修产生一条记录, 每个记录包含时间, 顾客ID和维修金额, 要取出每个顾客的维修次数和最后一次维修时的金额
- 场景2: 还是上面的维修记录表, 要取出每个顾客的每次维修之间的时间间隔
- 场景3: 一个用户账户的交易流水表, 要求每个小时的交易笔数和平均收支金额, 这个平均数的统计范围是两个小时(整点时间的前后一个小时)
使用窗函数直接SQL中使用窗函数就能解决这些问题, 否则需要使用临时表, 函数或存储过程进行处理.
窗函数
PostgreSQL 从2010年的版本8开始就支持窗函数了.
文档
详细说明建议查看官方文档 https://www.postgresql.org/docs/current/tutorial-window.html
函数说明
窗函数(window function)的计算方式与传统的单行和聚合不同
- 窗函数是在当前表中, 基于当前行的相关行的计算, 注意是基于多行的计算
- 属于一种聚合计算, 可以使用聚合类型的函数(aggregate function)
- 使用窗函数并不会导致结果的聚合, 也就是结果依然是当前的行结构
所以综合的说, 窗口函数就是在行的基础上, 允许对多行数据进行计算. 下面是一个简单的窗函数例子, 将每个员工的薪资与其所在的部门的平均薪资进行比较
SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;
关键词
使用窗函数时会用到的一些关键词
- OVER 前面的查询基于后面的窗口
- PARTITION BY 类似于 GROUP BY 的语义, 专用于窗口的分组
- ORDER BY 窗内的排序依据, 依据的字段决定了 RANGE 的类型
- RANGE ... PRECEDING 在当前值之前的范围, 基准是当前记录这个 ORDER BY 字段的值
- RANGE ... FOLLOWING 在当前值之后的范围, 基准是当前记录这个 ORDER BY 字段的值
- RANGE BETWEEN ... PRECEDING AND ... FOLLOWING 前后范围的组合
- WINDOW 将窗口命名为变量, 可以在 SELECT 中重复使用
示例
按窗口打序号
功能: 将数据按指定的字段分组, 再按另一个字段排列, 给每个分组里的数据打上序号.
这是一个常用技巧, 例如要计算各组内记录之间的时间间隔, 但是用时间不方便join, 打完序号后就可以用序号join了
SELECT
ROW_NUMBER() OVER w1 AS rn,
sample_01.*
FROM
sample_01
WINDOW
w1 AS (PARTITION BY field_name ORDER BY created_at ASC);
简单时间窗口统计
功能: 将数据表按指定字段(日期类型)进行排序, 然后基于每个记录的这个字段创建一个固定宽度的时间窗口, 对窗口内的多个记录进行统计
统计单个字段, 可以直接写在select中
SELECT
MAX(amount) OVER (ORDER BY traded_at RANGE '30 minutes' PRECEDING) AS amount_max,
*
FROM sample_01
WHERE card_num = '6210812500006111111'
基于时间窗口变量进行多字段统计
功能: 和前一个功能一样, 但是要进行多个不同的统计, 要重复用到这个窗口函数
如果要统计多个字段, 可以抽出单独的WINDOW
SELECT
MAX(rn) OVER w1 AS rn_max,
MAX(amount) OVER w1 AS amount_max,
AVG(amount) OVER w1 AS amount_avg,
*
FROM sample_01_diff
WINDOW
-- w1 AS (ORDER BY traded_at RANGE '30 minutes' PRECEDING)
w1 AS (PARTITION BY card_num ORDER BY traded_at RANGE BETWEEN '30 minutes' PRECEDING AND '30 minutes' FOLLOWING)
ORDER BY
rn ASC
在这个例子中
- 先依据 card_num 这个字段进行分区,
- 然后按 traded_at 这个字段进行排序,
- 对每个记录的 traded_at 值, 开启一个 RANGE, 包含前面的30分钟和后面的30分钟, RANGE 中能用的类型和 ORDER BY 的字段类型是相关的
- SELECT中的 MAX, MIN 等聚合函数, 是基于上面的 RANGE 进行的
In RANGE mode, these options require that the ORDER BY clause specify exactly one column. The offset specifies the maximum difference between the value of that column in the current row and its value in preceding or following rows of the frame. The data type of the offset expression varies depending on the data type of the ordering column. For numeric ordering columns it is typically of the same type as the ordering column, but for datetime ordering columns it is an interval. For example, if the ordering column is of type date or timestamp, one could write
RANGE BETWEEN '1 day' PRECEDING AND '10 days'FOLLOWING. The offset is still required to be non-null and non-negative, though the meaning of “non-negative” depends on its data type.
多个窗口多个字段同时统计
功能: 在前面的功能基础上, 同时存在多个时间窗口
SELECT
-- 1 hour
SUM(amount_in) OVER w1h AS h1_amount_in_sum,
SUM(
CASE
WHEN amount_in = 0 THEN 0
ELSE 1
END
) OVER w1h AS h1_amount_in_count,
SUM(amount_out) OVER w1h AS h1_amount_out_sum,
SUM(
CASE
WHEN amount_out = 0 THEN 0
ELSE 1
END
) OVER w1h AS h1_amount_out_count,
SUM(amount) OVER w1h AS h1_amount_sum,
COUNT(amount) OVER w1h AS h1_amount_count,
ROUND(AVG(amount) OVER w1h, 2) AS h1_amount_avg,
FIRST_VALUE(amount) OVER w1h AS h1_amount_first,
LAST_VALUE(amount) OVER w1h AS h1_amount_last,
MAX(amount) OVER w1h AS h1_amount_max,
MIN(amount) OVER w1h AS h1_amount_min,
-- 3 hour
SUM(amount_in) OVER w3h AS h3_amount_in_sum,
SUM(
CASE
WHEN amount_in = 0 THEN 0
ELSE 1
END
) OVER w3h AS h3_amount_in_count,
SUM(amount_out) OVER w3h AS h3_amount_out_sum,
SUM(
CASE
WHEN amount_out = 0 THEN 0
ELSE 1
END
) OVER w3h AS h3_amount_out_count,
SUM(amount) OVER w3h AS h3_amount_sum,
COUNT(amount) OVER w3h AS h3_amount_count,
ROUND(AVG(amount) OVER w3h, 2) AS h3_amount_avg,
FIRST_VALUE(amount) OVER w3h AS h3_amount_first,
LAST_VALUE(amount) OVER w3h AS h3_amount_last,
MAX(amount) OVER w3h AS h3_amount_max,
MIN(amount) OVER w3h AS h3_amount_min,
*
FROM sample_01
WINDOW
w1h AS (PARTITION BY card_num ORDER BY traded_at RANGE BETWEEN '30 minutes' PRECEDING AND '30 minutes' FOLLOWING),
w3h AS (PARTITION BY card_num ORDER BY traded_at RANGE BETWEEN '90 minutes' PRECEDING AND '90 minutes' FOLLOWING)
;
参考
- https://www.postgresql.org/docs/current/tutorial-window.html
- https://stackoverflow.com/questions/14989927/sql-sliding-window-finding-max-value-over-interval
- https://www.postgresqltutorial.com/postgresql-window-function/
- https://www.citusdata.com/blog/2018/06/01/fun-with-sql-window-functions-in-postgresql/
- https://tapoueh.org/blog/2013/08/understanding-window-functions/
PostgreSQL 的窗口函数 OVER, WINDOW, PARTITION BY, RANGE的更多相关文章
- mysql 分区 按 PARTITION BY RANGE (TO_DAYS(startTime))
to_days() Given a date date, returns a day number (the number of days since year 0). 给定一个date 日期,返回天 ...
- PostgreSQL>窗口函数的用法
PostgreSQL之窗口函数的用法 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9311281.html PostgreSQL的高级特性本准备三篇的(递归. ...
- PostgreSQL PARTITION 分区表
PostgreSQL 分区表,操作性相当便捷. 但只能在创建时决定是否为分区表,并决定分区条件字段,普通表创建后,不能在修改为分区表. Note:通过其他方法也可转化为分区表. 和其他数据库一样,分区 ...
- Oracle分区表之分区范围扫描(PARTITION RANGE ITERATOR)与位图范围扫描(BITMAP INDEX RANGE SCAN)
一.前言: 一开始分区表和位图索引怎么会挂钩呢?可能现实就是这么的不期而遇:比如说一张表的字段是年月日—‘yyyy-mm-dd’,重复率高吧,适合建位图索引吧,而且这张表数据量也不小,也适合转换成分区 ...
- [转帖]Greenplum :基于 PostgreSQL 的分布式数据库内核揭秘 (上篇)
Greenplum :基于 PostgreSQL 的分布式数据库内核揭秘 (上篇) https://www.infoq.cn/article/3IJ7L8HVR2MXhqaqI2RA 学长的文章.. ...
- (4.34)sql server窗口函数
关键词:sql server窗口函数,窗口函数,分析函数 如果分析函数不可用,那么可能是版本还不支持 Window Function 包含了 4 个大类.分别是: 1 - Rank Function ...
- 详解SQL操作的窗口函数
摘要:窗口函数是聚集函数的延伸,是更高级的SQL语言操作,主要用于AP场景下对数据进行一些分析.汇总.排序的功能. 本文分享自华为云社区<GaussDB(DWS) SQL进阶之SQL操作之窗口函 ...
- SQL Server Window Function 窗体函数读书笔记二 - A Detailed Look at Window Functions
这一章主要是介绍 窗体中的 Aggregate 函数, Rank 函数, Distribution 函数以及 Offset 函数. Window Aggregate 函数 Window Aggrega ...
- PostgreSQL Partitions
why we need partitions The first and most demanding reason to use partitions in a database is to inc ...
随机推荐
- 《深入理解ES6》笔记—— JavaScript中的类class(9)
ES5中的近类结构 ES5以及之前的版本,没有类的概念,但是聪明的JavaScript开发者,为了实现面向对象,创建了特殊的近类结构. ES5中创建类的方法:新建一个构造函数,定义一个方法并且赋值给构 ...
- CSS揭秘之《背景图案》
网格 html { background: #58a; background-image: linear-gradient(white 2px, transparent 0), linear-grad ...
- MapReduce在集群执行任务时报错:Initialization of all the collectors failed. Error in last collector was:java.lang.ClassCastException
报错信息详细: Error: java.io.IOException: Initialization of all the collectors failed. Error in last colle ...
- clone github代码库很慢,如何提速
博主,最近在搭建hyperledger fabric的环境,其中有一步就是clone github上的代码,但是在过程中发现clone是真的慢. 为此google了一圈,发一现一个好用的办法: 1.登 ...
- 基于LAMP离线部署zabbix3.2.11
zabbix是个什么东西这里不再赘述,先安装lamp再安装zabbix. 1. 安装依赖插件(把下面PHP那些依赖库全部都装了) #yum install -y gcc gcc-c++ opens ...
- 讲解CPU之NUMA硬件体系以及机制(lscpu查看相关信息)
先看看从系统层面反映出来的numa cpu信息.采样机器为实体机.80核.128内存. [root@ht2 src]# lscpu Architecture: x86_64 #x86架构下的64位 C ...
- MySQL5.6复制技术
mysql复制功能介绍 我们可以通过为服务器配置主从即一个或多个备库的方式,以及主主结构来进行数据同步,将MySQL的数据分布到多个系统上去.复制过程中一台主库(master)服务器可以数据被同步到多 ...
- JAVA 进程线程详解
线程和进程 一.进程 进程是指运行中的程序,比如我们使用QQ,就启动该进程分配内存空间. 进程是程序的一次执行过程,或是正在运行的一个程序.是一个动态的过程:有它自升的产生,存在和消亡的过程 二.线程 ...
- 关于croptool无法裁剪分辩率过低的图片
在使用croptool.js时,如果图片的分辨率过低,则无法裁剪超过分辨率的大小.比如说 function chooseImg(event){ var files = event.files || e ...
- 华为组播实验,PIM-DM组播实验
一.配置VLAN,并将端口加入VLAN LSW5: system vlan batch 10 to 100 int g 0/0/1 port link-type trunk port trunk al ...