最近在数据处理中用到了窗函数, 把使用方法记录一下, 暂时只有分组排序和滑动时间窗口的例子, 以后再逐步添加

场景

在SQL查询时, 会遇到有两类需要分组统计的场景, 在之前的SQL语法中是不方便实现的

  1. 场景1: 顾客维修设备的记录表, 每次维修产生一条记录, 每个记录包含时间, 顾客ID和维修金额, 要取出每个顾客的维修次数和最后一次维修时的金额
  2. 场景2: 还是上面的维修记录表, 要取出每个顾客的每次维修之间的时间间隔
  3. 场景3: 一个用户账户的交易流水表, 要求每个小时的交易笔数和平均收支金额, 这个平均数的统计范围是两个小时(整点时间的前后一个小时)

使用窗函数直接SQL中使用窗函数就能解决这些问题, 否则需要使用临时表, 函数或存储过程进行处理.

窗函数

PostgreSQL 从2010年的版本8开始就支持窗函数了.

文档

详细说明建议查看官方文档 https://www.postgresql.org/docs/current/tutorial-window.html

函数说明

窗函数(window function)的计算方式与传统的单行和聚合不同

  1. 窗函数是在当前表中, 基于当前行的相关行的计算, 注意是基于多行的计算
  2. 属于一种聚合计算, 可以使用聚合类型的函数(aggregate function)
  3. 使用窗函数并不会导致结果的聚合, 也就是结果依然是当前的行结构

所以综合的说, 窗口函数就是在行的基础上, 允许对多行数据进行计算. 下面是一个简单的窗函数例子, 将每个员工的薪资与其所在的部门的平均薪资进行比较

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

在这个例子中

  1. 先依据 card_num 这个字段进行分区,
  2. 然后按 traded_at 这个字段进行排序,
  3. 对每个记录的 traded_at 值, 开启一个 RANGE, 包含前面的30分钟和后面的30分钟, RANGE 中能用的类型和 ORDER BY 的字段类型是相关的
  4. 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)
;

参考

PostgreSQL 的窗口函数 OVER, WINDOW, PARTITION BY, RANGE的更多相关文章

  1. 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 日期,返回天 ...

  2. PostgreSQL>窗口函数的用法

    PostgreSQL之窗口函数的用法 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9311281.html PostgreSQL的高级特性本准备三篇的(递归. ...

  3. PostgreSQL PARTITION 分区表

    PostgreSQL 分区表,操作性相当便捷. 但只能在创建时决定是否为分区表,并决定分区条件字段,普通表创建后,不能在修改为分区表. Note:通过其他方法也可转化为分区表. 和其他数据库一样,分区 ...

  4. Oracle分区表之分区范围扫描(PARTITION RANGE ITERATOR)与位图范围扫描(BITMAP INDEX RANGE SCAN)

    一.前言: 一开始分区表和位图索引怎么会挂钩呢?可能现实就是这么的不期而遇:比如说一张表的字段是年月日—‘yyyy-mm-dd’,重复率高吧,适合建位图索引吧,而且这张表数据量也不小,也适合转换成分区 ...

  5. [转帖]Greenplum :基于 PostgreSQL 的分布式数据库内核揭秘 (上篇)

    Greenplum :基于 PostgreSQL 的分布式数据库内核揭秘 (上篇) https://www.infoq.cn/article/3IJ7L8HVR2MXhqaqI2RA 学长的文章.. ...

  6. (4.34)sql server窗口函数

    关键词:sql server窗口函数,窗口函数,分析函数 如果分析函数不可用,那么可能是版本还不支持 Window Function 包含了 4 个大类.分别是: 1 - Rank Function ...

  7. 详解SQL操作的窗口函数

    摘要:窗口函数是聚集函数的延伸,是更高级的SQL语言操作,主要用于AP场景下对数据进行一些分析.汇总.排序的功能. 本文分享自华为云社区<GaussDB(DWS) SQL进阶之SQL操作之窗口函 ...

  8. SQL Server Window Function 窗体函数读书笔记二 - A Detailed Look at Window Functions

    这一章主要是介绍 窗体中的 Aggregate 函数, Rank 函数, Distribution 函数以及 Offset 函数. Window Aggregate 函数 Window Aggrega ...

  9. PostgreSQL Partitions

    why we need partitions The first and most demanding reason to use partitions in a database is to inc ...

随机推荐

  1. ES6-11学习笔记--异步迭代

    ES9提供异步迭代: for await of Symbol.asyncIterator   function getPromise(time) { return new Promise((resol ...

  2. ccf颁奖晚会

    感想: 首先,十分感谢学校给我们参加比赛的机会,给予我们这次难能可贵的学习机会,第一次在这么大型的赛场中展现自己,我们也不免有些紧张.从最开始在线上进行模型训练,到我们不远千里在江苏进行场地的勘察,到 ...

  3. 【每日日报】第三十二天---DataOutputStream写文件

    1 今天继续看书 DataOutputStream写文件 1 package File; 2 import java.io.IOException; 3 import java.io.FileOutp ...

  4. 记住用户名和登录密码+虚拟机没有root权限解决办法

    今日所学: 记住用户名和登录密码 用adb查看保存文件内容 如何使用adb 如何安装adb-百度经验 遇到的问题: 用adb查看文件时,没有权限访问data文件 出现原因:google play虚拟机 ...

  5. C2678 二进制“<”: 没有找到接受“const ***”类型的左操作数的运算符解决办法

    正确代码如下:#include<iostream> #include<string> #include<map> using namespace std; /*仿函 ...

  6. jboss学习1之EJB和JBOSS的宏观理解

    一.中间件(Middleware)         先来看一张图:         中间件,也就是图中的Middleware,他的作用是什么呢?        简单来说,中间件就是操作系统和应用程序之 ...

  7. C语言 之 多线程编程

    一.基础知识 计算机的核心是CPU,承担了所有的计算任务. 操作系统是计算机的管理者,负责任务的调度.资源的分配和管理,统领整个计算机硬件. 应用程序则是具有某种功能的程序,程序是运行于操作系统之上的 ...

  8. 【Unity原神AR开发实战 2022】下载原神模型,PMX转FBX,导入到Unity,AR设置,测试应用程序,生成应用程序

    文章目录 一.前言 二.模型下载 1.官网下载 2.模之屋官方下载 3.第三方链接 三.pmx转fbx 1.Blender插件CATS的下载与安装 2.pmx模型的导入 四.Unity开发部分 1.V ...

  9. DDD(Domain-Driven Design) 领域驱动设计

    DDD(Domain-Driven Design) 领域驱动设计 1. DDD(Domain-Driven Design)是什么? DDD是Eric Evans在2003年出版的<领域驱动设计: ...

  10. Ubu18安装RabbitMQ

    RabbitMQ安装 https://www.jianshu.com/p/5c8c4495827f 安装 RabbitMQ基于erlang语言,需要先安装 sudo apt-get install e ...