PostgreSQL 聚合函数讲解 - 7 窗口反聚合

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介:
聚合函数的最后一个分类, Hypothetical-Set Aggregate Functions.
这类聚合函数还有对应的窗口函数, 首先来看一下对应窗口函数的用法.
rank() bigint rank of the current row with gaps; same as row_number of its first peer
dense_rank() bigint rank of the current row without gaps; this function counts peer groups
percent_rank() double precision relative rank of the current row: (rank - 1) / (total rows - 1)
cume_dist() double precision relative rank of the current row: (number of rows preceding or peer with current row) / (total rows)
the four ranking functions are defined so that they give the same answer for any two peer rows.
 
 

rank 返回值在分组内的等级, 如果值有重复的话, 跳级处理.
dense_rank 返回值在分组内的等级, 如果值有重复的话, 不跳级处理.
percent_rank 返回 (rank - 1) / (total rows - 1), rank指当前rank值, rows指当前组的记录数
cume_dist 返回(number of rows preceding or peer with current row) / (total rows), 即截至当前记录等级一共有多少行除以本组的总行数.

看一个例子比较明白.
 
 

postgres=# select *,row_number() over(partition by info order by id),rank() over(partition by info order by id),dense_rank() over(partition by info order by id),percent_rank() over(partition by info order by id),cume_dist() over(partition by info order by id) from test;
  id  | info  | row_number | rank | dense_rank | percent_rank |     cume_dist     
------+-------+------------+------+------------+--------------+-------------------
    1 | test1 |          1 |    1 |          1 |            0 |                 1
    2 | test2 |          1 |    1 |          1 |            0 | 0.444444444444444
    2 | test2 |          2 |    1 |          1 |            0 | 0.444444444444444
    2 | test2 |          3 |    1 |          1 |            0 | 0.444444444444444
    2 | test2 |          4 |    1 |          1 |            0 | 0.444444444444444
    3 | test2 |          5 |    5 |          2 |          0.5 | 0.555555555555556
    4 | test2 |          6 |    6 |          3 |        0.625 | 0.666666666666667
    5 | test2 |          7 |    7 |          4 |         0.75 | 0.777777777777778
    6 | test2 |          8 |    8 |          5 |        0.875 | 0.888888888888889
    7 | test2 |          9 |    9 |          6 |            1 |                 1
    8 | test3 |          1 |    1 |          1 |            0 |               0.5
  100 | test3 |          2 |    2 |          2 |            1 |                 1
 1000 | test4 |          1 |    1 |          1 |            0 |                 1
(13 rows)

算法 : 
 
 

info='test2'这个组为例 : 
    2 | test2 |          1 |    1 |          1 |            0 | 0.444444444444444
    2 | test2 |          2 |    1 |          1 |            0 | 0.444444444444444
    2 | test2 |          3 |    1 |          1 |            0 | 0.444444444444444
    2 | test2 |          4 |    1 |          1 |            0 | 0.444444444444444
id=2 rankdense_rank都是1.
percent_rank 返回 (rank - 1) / (total rows - 1), rank指当前rank值, rows指当前组的记录数
cume_dist 返回(number of rows preceding or peer with current row) / (total rows), 截至当前记录等级一共有多少行除以本组的总行数.
所以
percent_rank = (1-1)/(9-1)=0
cume_dist = (4)/(9) = 0.444444444444444

    3 | test2 |          5 |    5 |          2 |          0.5 | 0.555555555555556
rank = 5, 跳级
dense_rank = 2, 不跳级
percent_rank = (5-1)/(9-1)=0.5
cume_dist = (5)/(9) = 0.555555555555556


接下来回到正题, 我们看看这些窗口函数的另一种用法, 聚合用法.

Each of the aggregates listed in Table 9-52 is associated with a window function of the same name defined in Section 9.21. In each case, the aggregate result is the value that the associated window function would have returned for the "hypothetical" row constructed from args, if such a row had been added to the sorted group of rows computed from the sorted_args.

Table 9-52. Hypothetical-Set Aggregate Functions

Function Direct Argument Type(s) Aggregated Argument Type(s) Return Type Description
rank(args) WITHIN GROUP (ORDER BY sorted_args) VARIADIC "any" VARIADIC "any" bigint rank of the hypothetical row, with gaps for duplicate rows
dense_rank(args) WITHIN GROUP (ORDER BY sorted_args) VARIADIC "any" VARIADIC "any" bigint rank of the hypothetical row, without gaps
percent_rank(args) WITHIN GROUP (ORDER BYsorted_args) VARIADIC "any" VARIADIC "any" double precision relative rank of the hypothetical row, ranging from 0 to 1
cume_dist(args) WITHIN GROUP (ORDER BY sorted_args) VARIADIC "any" VARIADIC "any" double precision relative rank of the hypothetical row, ranging from 1/N to 1

For each of these hypothetical-set aggregates, the list of direct arguments given in args must match the number and types of the aggregated arguments given in sorted_args. Unlike most built-in aggregates, these aggregates are not strict, that is they do not drop input rows containing nulls. Null values sort according to the rule specified in the ORDER BY clause.

这些用法比较奇特, 其实是要返回给定参数在集合中的位置.
例如  : 
 
 

1
2
3
4
5

如果我们给一个参数值是2.2, 应该排在以上数据中的第三行.
例子 : 
 
 

postgres=# select * from test order by info,id;
  id  | info  
------+-------
    1 | test1
    2 | test2
    2 | test2
    2 | test2
    2 | test2
    3 | test2
    4 | test2
    5 | test2
    6 | test2
    7 | test2
    8 | test3
  100 | test3
 1000 | test4
(13 rows)

postgres=# select info,rank(4.9) within group (order by id),dense_rank(4.9) within group (order by id) from test group by info;
 info  | rank | dense_rank 
-------+------+------------
 test1 |    2 |          2
 test2 |    7 |          4
 test3 |    1 |          1
 test4 |    1 |          1
(4 rows)

4.9在test1这个分组, 排名第2, 并且这个分组只有1个值, 所以没有gap.
重点关注test2这个组, 这个组有9个值, 其中有4个重复值2, 所以4.9在这里排名需要考虑gap. 
rank 返回7, 即4.9在这里考虑GAP排名第7
dense_rank 返回4, 即4.9在这里不考虑GAP排名第4.
又如 : 
 
 

postgres=# select info,rank(5) within group (order by id),dense_rank(5) within group (order by id) from test group by info;
 info  | rank | dense_rank 
-------+------+------------
 test1 |    2 |          2
 test2 |    7 |          4
 test3 |    1 |          1
 test4 |    1 |          1
(4 rows)
postgres=# select info,rank(5.1) within group (order by id),dense_rank(5.1) within group (order by id) from test group by info;
 info  | rank | dense_rank 
-------+------+------------
 test1 |    2 |          2
 test2 |    8 |          5
 test3 |    1 |          1
 test4 |    1 |          1
(4 rows)


最后要看计算0~1代表位置的聚合函数percent_rank和cume_dist.
算法
percent_rank 返回 (rank - 1) / (total rows - 1), rank指当前rank值, rows指当前组的记录数
cume_dist 返回(number of rows preceding or peer with current row) / (total rows), 截至当前记录等级一共有多少行除以本组的总行数.
例子1 : 
 
 

postgres=# select info,rank(4.9) within group (order by id),dense_rank(4.9) within group (order by id),percent_rank(4.9) within group (order by id),cume_dist(4.9) within group (order by id) from test group by info;
 info  | rank | dense_rank |   percent_rank    |     cume_dist     
-------+------+------------+-------------------+-------------------
 test1 |    2 |          2 |                 1 |                 1
 test2 |    7 |          4 | 0.666666666666667 |               0.7
 test3 |    1 |          1 |                 0 | 0.333333333333333
 test4 |    1 |          1 |                 0 |               0.5
(4 rows)

同样以test2为分组, 讲解算法. 把4.9插入到这个分组后. 数据应该变成 : 
 
 

    2 | test2
    2 | test2
    2 | test2
    2 | test2
    3 | test2
    4 | test2
    4.9 | test2  # 计算位置
    5 | test2
    6 | test2
    7 | test2

一共10行.
percent_rank 返回 (rank - 1) / (total rows - 1), rank指当前rank值, rows指当前组的记录数
cume_dist 返回(number of rows preceding or peer with current row) / (total rows), 截至当前记录等级一共有多少行除以本组的总行数.
所以4.9对应的percent_rank 和 cume_dist 分别为 : 
percent_rank = (rank - 1) / (total rows - 1) = (7-1)/(10-1) = 0.666666666666667 
cume_dist = (7)/10 = 0.7

例子2 : 
 
 

postgres=# select info,rank(5) within group (order by id),dense_rank(5) within group (order by id),percent_rank(5) within group (order by id),cume_dist(5) within group (order by id) from test group by info;
 info  | rank | dense_rank |   percent_rank    |     cume_dist     
-------+------+------------+-------------------+-------------------
 test1 |    2 |          2 |                 1 |                 1
 test2 |    7 |          4 | 0.666666666666667 |               0.8
 test3 |    1 |          1 |                 0 | 0.333333333333333
 test4 |    1 |          1 |                 0 |               0.5
(4 rows)

插入计算值5后, 数据变成
    2 | test2
    2 | test2
    2 | test2
    2 | test2
    3 | test2
    4 | test2
    5 | test2  # 计算位置, 即参数值
    5 | test2
    6 | test2
    7 | test2

依旧10行. 但是截至当前记录等级一共有多少行? 注意是8了.
percent_rank = (rank - 1) / (total rows - 1) = (7-1)/(10-1) = 0.666666666666667 
cume_dist = (8)/10 = 0.8

例子3 : 
 
 

postgres=# select info,rank(5.1) within group (order by id),dense_rank(5.1) within group (order by id),percent_rank(5.1) within group (order by id),cume_dist(5.1) within group (order by id) from test group by info;
 info  | rank | dense_rank |   percent_rank    |     cume_dist     
-------+------+------------+-------------------+-------------------
 test1 |    2 |          2 |                 1 |                 1
 test2 |    8 |          5 | 0.777777777777778 |               0.8
 test3 |    1 |          1 |                 0 | 0.333333333333333
 test4 |    1 |          1 |                 0 |               0.5
(4 rows)

插入计算值5.1后, 数据变成 : 
    2 | test2
    2 | test2
    2 | test2
    2 | test2
    3 | test2
    4 | test2
    5 | test2
    5.1 | test2  # 计算位置, 即参数值
    6 | test2
    7 | test2

结果自己验证吧.

例子4 : 
 
  

postgres=# select info,rank(5) within group (order by id desc),dense_rank(5) within group (order by id desc),percent_rank(5) within group (order by id desc),cume_dist(5) within group (order by id desc) from test group by info;
 info  | rank | dense_rank |   percent_rank    | cume_dist 
-------+------+------------+-------------------+-----------
 test1 |    1 |          1 |                 0 |       0.5
 test2 |    3 |          3 | 0.222222222222222 |       0.4
 test3 |    3 |          3 |                 1 |         1
 test4 |    2 |          2 |                 1 |         1
(4 rows)

插入计算值5后, 数据变成 : 
 
  

    7 | test2
    6 | test2
    5 | test2  # 注意, 这才是计算位置, 即插入位置.
    5 | test2
    4 | test2
    3 | test2
    2 | test2
    2 | test2
    2 | test2
    2 | test2

结果自己验证吧.

[参考]
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
关系型数据库 PostgreSQL
PostgreSQL listagg within group (order by) 聚合兼容用法 string_agg ( order by) - 行列变换,CSV构造...
标签 PostgreSQL , order-set agg , listagg , string_agg , order 背景 listagg — Rows to Delimited Strings The listagg function transforms values from a g...
6222 0
|
关系型数据库 大数据 PostgreSQL
PostgreSQL16-新特性-并行聚合
PostgreSQL16-新特性-并行聚合
147 0
|
关系型数据库 PostgreSQL
【重新发现PostgreSQL之美】- 48 聚合、窗口过滤器
大家好,这里是重新发现PostgreSQL之美 - 48 聚合、窗口过滤器
|
SQL 分布式计算 并行计算
PostgreSQL 并行计算解说 之9 - parallel 自定义并行聚合
标签 PostgreSQL , cpu 并行 , smp 并行 , 并行计算 , gpu 并行 , 并行过程支持 背景 PostgreSQL 11 优化器已经支持了非常多场合的并行。简单估计,已支持27余种场景的并行计算。 parallel seq scan parallel index scan
611 0
|
SQL 关系型数据库 PostgreSQL
PostgreSQL 空间聚合性能 - 行政区、电子围栏 空间聚合 - 时间、空间热力图
标签 PostgreSQL , 空间聚合 , 空间热力图 , 行政区 , 电子围栏 背景 某个时间区间(或其他条件),出现在某些围栏、行政区(多边形信息)中的对象(空间点信息)有多少,按颜色深浅渲染这些多边形。
2479 0
|
物联网 关系型数据库 流计算
PostgreSQL pipelinedb 流计算插件 - IoT应用 - 实时轨迹聚合
标签 PostgreSQL , IoT , 轨迹聚合 , pipelinedb , 流计算 , 实时聚合 背景 IoT场景,车联网场景,共享单车场景,人的行为位点等,终端实时上报的是孤立的位点,我们需要将其补齐成轨迹。
1833 0
|
SQL 移动开发 关系型数据库
PostgreSQL sharding : citus 系列3 - 窗口函数调用限制 与 破解之法(套用gpdb执行树,分步执行)
标签 PostgreSQL , citus , 窗口函数 背景 窗口函数是分析场景常用的,目前(citus 7.5)仅支持两种场景使用window函数, 1、partition by 必须是分布键。
1520 0
|
算法 关系型数据库 测试技术
PostgreSQL 无会话、有会话模式 - 客服平均响应速度(RT)实时计算实践(窗口查询\流计算)
标签 PostgreSQL , 无会话 , 客服响应速度 , 触发器 , rule , 窗口查询 背景 通常客服系统可能存在一对多,多对多的情况。 例如, 我们在使用淘宝时,与店家交流时,你根本不知道后面的小二是一个人还是多个人共用一个账号,还有可能是多个人使用了多个账号但是对消费者只看到一个。
1094 0
|
并行计算 关系型数据库 PostgreSQL

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版