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

场景

在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. java中异常(Exception)的定义,意义和用法。举例

    1.异常(Exception)的定义,意义和用法 我们先给出一个例子,看看异常有什么用? 例:1.1- public class Test {    public static void main(S ...

  2. java中内部类中还有内部类请给实例!

    2.当内部类中还有一个内部类,下面给出了一个实例.[新手可忽略不影响继续学习](以下多出代码, 用蓝色标记)例2.2:class ShellMark_to_win {    int shell_x = ...

  3. 底部footer挡住上面内容的bug

    当设置底部footer的样式为: .footer{ position: fixed; height: 49px; bottom: 0; background: #fff; } 这样会挡住上面的内容,修 ...

  4. 性能优化之html、css、js三者的加载顺序

    前言 我们知道一个页面通常由,html,css,js三部分组成,一般我们会把css文件放在head头部加载,而js文件则放在页面的最底部加载,想要知道为什么大家都会不约而同的按照这个标准进行构建页面, ...

  5. 帝国cms插件 解决后台修改信息时内容关键字不替换的问题

    很多站长是不是发现了帝国cms增加信息时,是有关键词替换的,这样是有利于网站优化排名. 但是在后台格式化数据之后,再去进行修改之后,对不起,内容关键字就实效了. 针对这一问题,解决方案如下: 找到 / ...

  6. MPU9250/MPU6050与运动数据处理与卡尔曼滤波(1)

    第一篇--概述和MPU6050及其自带的DMP输出四元数 概述 InvenSense(国内一般译为应美盛)公司产的数字运动传感器在国内非常流行,我用过它的两款,9250和6050.出于被国产芯片惯坏的 ...

  7. Python Windows 快捷键自动给剪贴板(复制)图片添加水印

    编写一个能在windows上使用的按下快捷键自动给剪贴板(复制)的图片添加水印的小工具.plyer.PIL.pyinstaller.pynput.win32clipboard库.记录自己踩过的坑,部分 ...

  8. 计算机编码规则之:Base64编码

    目录 简介 Base64和它的编码原理 Base64的变体 Base64的编码细节 总结 简介 我们知道计算机中的文件可以分为两种,一种是人肉眼可读的文本类文件,一种是肉眼不可读的二进制文件.一般来说 ...

  9. Elasticsearch上手指南

    目录 ElasticStack及Elasticsearch介绍 Elasticsearch安装 Elasticsearch入门 Elasticsearch配置 Elasticsearch REST A ...

  10. docker入门_image、container相关命令

    docker入门_image.container相关命令 镜像仓库服务.镜像仓库.镜像相关概念 镜像仓库服务:docker镜像仓库服务.阿里云镜像服务 镜像仓库:docker镜像仓库服务中会有很多仓库 ...