• PostgreSQL学习笔记——窗口函数


    在学习窗口函数之前,我们新建一个Product表并往其中插入一些数据:

    drop table if exists Product;
    create table Product 
    (
    	product_id		char(4)			not null,
    	product_name	varchar(100)	not null,
    	product_type	varchar(32)		not null,
    	sale_price		integer			,
    	purchase_price	integer			,
    	regist_date		date			,
    	primary key (product_id)
    );
    
    begin transaction;
    
    insert into Product values ('0001', 'T恤衫', '衣服', 100, 50, '2018-10-10');
    insert into Product values ('0002', '打孔器', '办公用品', 50, 30, '2018-10-25');
    insert into Product values ('0003', '运动T恤', '衣服', 400, 280, '2018-10-01');
    insert into Product values ('0004', '菜刀', '厨房用具', 300, 280, '2018-11-11');
    insert into Product values ('0005', '高压锅', '厨房用具', 680, 500, '2018-10-22');
    insert into Product values ('0006', '叉子', '厨房用具', 50, NULL, '2018-10-08');
    insert into Product values ('0007', '擦菜纸', '厨房用具', 88, 66, '2018-11-12');
    insert into Product values ('0008', '圆珠笔', '办公用品', 100, NULL, '2018-10-25');
    
    commit;
    

    什么是窗口函数

    窗口函数 也称为 OLAP函数
    OLAP是OnLine Analytical Processing的简称,意思是对数据库数据进行实时分析处理。例如:市场分析、创建财务报表、创建计划等日常性商务工作。
    窗口函数就是为了实现OLAP而添加的标准SQL功能。

    窗口函数的语法

    <窗口函数> OVER ([PARTITION BY <列清单>] ORDER BY <排序用列清单>)
    

    窗口函数大体分为以下两种:

    1. 能够作为窗口函数的聚合函数(SUM、AVG、COUNT、MAX、MIN)
    2. RANK、DENSE_RANK、ROW_NUMBER等 专用窗口函数

    语法的基本使用——使用RANK函数

    RANK是用来计算记录排序的函数。
    对于Product表中的8件商品,使用如下SQL可以根据不同的商品种类(product_type),按照销售单价(sale_price)从低到高的顺序排序:

    select product_name, product_type, sale_price, 
    		rank() over (partition by product_type
    						order by sale_price) as ranking
    	from Product;
    

    结果如下所示:

    product_name product_type sale_price ranking
    打孔器 办公用品 50 1
    圆珠笔 办公用品 100 2
    叉子 厨房用具 50 1
    擦菜纸 厨房用具 88 2
    菜刀 厨房用具 300 3
    高压锅 厨房用具 680 4
    T恤衫 衣服 100 1
    运动T恤 衣服 400 2

    以厨房用具为例,最便宜的“叉子”排在第1位,最贵的“高压锅”排在第4位,确实按照我们的要求进行了排序。

    • PARTITION BY能够设定排序的对象范围。
    • ORDER BY能够指定按照哪一列、何种顺序进行排序。

    无需指定PARTITION BY

    我们删除上面SQL的PARTITION BY子句,如下:

    select product_name, product_type, sale_price, 
    		rank() over (
    						order by sale_price) as ranking
    	from Product;
    

    结果如下:

    product_name product_type sale_price ranking
    叉子 厨房用具 50 1
    打孔器 办公用品 50 1
    擦菜纸 厨房用具 88 3
    T恤衫 衣服 100 4
    圆珠笔 办公用品 100 4
    菜刀 厨房用具 300 6
    运动T恤 衣服 400 7
    高压锅 厨房用具 680 8

    之前我们得到的是按照商品种类分组后的排序,而这次变成了全部商品的排序。

    专用窗口函数的种类

    接下来我们来总结以下具有代表性的专用窗口函数:

    RANK函数
    计算排序时,如果存在相同位次的记录,则会跳过之后的位次。
    例)有3条记录排在第一位时,1位、1位、1位、4位……

    DENSE_RANK函数
    计算排序时,即使存在相同位次的记录,也不会跳过之后的位次。
    例)有3条记录排在第一位时,1位、1位、1位、2位……

    ROW_NUMBER函数
    赋予唯一的连续位次。
    例)有3条记录排在第一位时,1位、2位、3位、4位……

    窗口函数的适用范围

    窗口函数只能放在SELECT子句之中。
    也就是说,这类函数不能再WHERE子句或者GROUP BY子句中使用。

    作为窗口函数使用的聚合函数

    将SUM函数作为聚合函数使用:

    select product_name, product_type, sale_price, 
    		sum(sale_price) over (order by product_id) as current_sum
    	from Product;
    

    结果:

    product_name product_type sale_price current_sum 解释
    T恤衫 衣服 100 100 <--100
    打孔器 办公用品 50 150 <--100+50
    运动T恤 衣服 400 550 <--100+50+400
    菜刀 厨房用具 300 850
    高压锅 厨房用具 680 1530
    叉子 厨房用具 50 1580
    擦菜纸 厨房用具 88 1668
    圆珠笔 办公用品 100 1768

    窗口函数一般都会使用这种称为 累计 的统计方法。

    将AVG函数作为窗口函数使用:

    select product_name, product_type, sale_price, 
    		avg(sale_price) over (order by product_id) as current_avg
    	from Product;
    

    结果:

    product_name product_type sale_price current_avg 解释
    T恤衫 衣服 100 100.0000000000000000 <--(100)/1
    打孔器 办公用品 50 75.0000000000000000 <--(100+50)/2
    运动T恤 衣服 400 183.3333333333333333 <--(100+50+400)/3
    菜刀 厨房用具 300 212.5000000000000000
    高压锅 厨房用具 680 306.0000000000000000
    叉子 厨房用具 50 263.3333333333333333
    擦菜纸 厨房用具 88 238.2857142857142857
    圆珠笔 办公用品 100 221.0000000000000000

    从以上两个结果中我们可以看到,current_sum和current_avg的计算方法都是包含“排在自己之上”的记录。像这样的“自身记录( 当前记录 )”作为基准进行统计,就是将聚合函数当作窗口函数使用时的最大特征。

    计算移动平均

    执行如下SQL:

    select product_name, product_type, sale_price, 
    		avg(sale_price) over (order by product_id
    								rows 2 preceding) as moving_avg
    	from Product;
    

    结果:

    product_name product_type sale_price moving_avg 解释
    T恤衫 衣服 100 100.0000000000000000 <-- (100)/1
    打孔器 办公用品 50 75.0000000000000000 <-- (100+50)/2
    运动T恤 衣服 400 183.3333333333333333 <-- (100+50+400)/3
    菜刀 厨房用具 300 250.0000000000000000 <-- (50+400+300)/3
    高压锅 厨房用具 680 460.0000000000000000 <-- (400+300+600)/3
    叉子 厨房用具 50 343.3333333333333333
    擦菜纸 厨房用具 88 272.6666666666666667
    圆珠笔 办公用品 100 79.3333333333333333

    可以发现第4行数据和之前的结果不一样了,这是因为我们指定了“框架”,将汇总对象限定为了“最靠近的3行”。
    这里我们使用 ROW (“行”)和 PRECEDING (“之前”)两个关键字,限定的查询的结果只包含本身这行和它之前的两行(如果有的话)。
    使用关键字 FOLLOWING (“之后”)替换 PRECEDING ,就可以指定“包含之后的几行”。

    示例:将当前记录的前后行作为汇总对象:

    select product_name, product_type, sale_price, 
    		avg(sale_price) over (order by product_id
    								rows between 1 preceding and 1 following) as moving_avg
    	from Product;
    

    结果:

    product_name product_type sale_price moving_avg
    T恤衫 衣服 100 75.0000000000000000
    打孔器 办公用品 50 183.3333333333333333
    运动T恤 衣服 400 250.0000000000000000
    菜刀 厨房用具 300 460.0000000000000000
    高压锅 厨房用具 680 343.3333333333333333
    叉子 厨房用具 50 272.6666666666666667
    擦菜纸 厨房用具 88 79.3333333333333333
    圆珠笔 办公用品 100 94.0000000000000000

    两个ORDER BY

    OVER子句中的ORDER BY只是用来决定窗口函数按照什么样的顺序进行计算的,对结果的顺序并没有影响。所以,要对结果进行排序,还需要添加另一个ORDER BY子句,例:

    select product_name, product_type, sale_price, 
    		rank() over (order by sale_price) as ranking
    	from Product
    	order by ranking;
    

    结果:

    product_name product_type sale_price ranking
    叉子 厨房用具 50 1
    打孔器 办公用品 50 1
    擦菜纸 厨房用具 88 3
    T恤衫 衣服 100 4
    圆珠笔 办公用品 100 4
    菜刀 厨房用具 300 6
    运动T恤 衣服 400 7
    高压锅 厨房用具 680 8
  • 相关阅读:
    宏大的目标
    java tcp ip网络编程(二) 套接字的基本使用
    java socket编程(一)简介
    是么是 API 和 SDK
    Mac 下显示隐藏文件
    iOS-事务相关
    iOS测试一段代码的运行时间
    sqlite3 语句总结
    iOS-scrollview及其子类适配iOS7
    OAuth2.0授权和SSO授权
  • 原文地址:https://www.cnblogs.com/zifeiy/p/9851847.html
Copyright © 2020-2023  润新知