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

猜你喜欢

转载自www.cnblogs.com/zifeiy/p/9851847.html
今日推荐