Oracle开发之:窗口函数

1,测试环境:
SQL> create table win_order(
     month number(2),
     total_sales number);

 2,输入数据:
insert into win_order values(1,623141);
insert into win_order values(2,423124);
insert into win_order values(3,323214);
insert into win_order values(4,212314);
insert into win_order values(5,654314);
insert into win_order values(6,122134);
insert into win_order values(7,859234);
insert into win_order values(8,752314);
insert into win_order values(9,365314);
insert into win_order values(10,265314);
insert into win_order values(11,563114);
insert into win_order values(12,595314); 

 3,测试语句:

      我们前面使用了sum(sum(sal)) over (partition by deptno) 来统计每个部门的总额。现在我们要统计的不单是每个部门,而是所有分区,partition by region_id在这里不起作用了。

Oracle为这种情况提供了一个子句:rows between ... preceding and ... following。从字面上猜测它的意思是:在XXX之前和XXX之后的所有记录,实际情况如何让我们通过示例来验证:

SQL> select month,
            sum(total_sales) month_sales,
            sum(sum(total_sales))over (order by month
            rows between unbounded preceding and unbounded following) total_sale
         from win_order group by month;
 
MONTH MONTH_SALES TOTAL_SALE
----- ----------- ----------
    1      623141    5758845
    2      423124    5758845
    3      323214    5758845
    4      212314    5758845
    5      654314    5758845
    6      122134    5758845
    7      859234    5758845
    8      752314    5758845
    9      365314    5758845
   10      265314    5758845
   11      563114    5758845
   12      595314    5758845
 
12 rows selected

高亮处的代码在这里发挥了关键作用,它告诉oracle统计从第一条记录开始至最后一条记录的每月销售额。这个统计在记录集形成的过程中执行了12次,这时相当费时的!但至少我们解决了问题。

    unbounded preceding and unbouned following的意思针对当前所有记录的前一条、后一条记录,也就是表中的所有记录。那么假如我们直接指定从第一条记录开始直至末尾呢?看看下面的结果:

SQL> select month,
              sum(total_sales) month_sales,
              sum(sum(total_sales))over (order by month
              rows between 1 preceding and unbounded following) total_sale
          from win_order group by month ;

MONTH MONTH_SALES TOTAL_SALE
----- ----------- ----------
    1      623141    5758845
    2      423124    5758845
    3      323214    5135704
    4      212314    4712580
    5      654314    4389366
    6      122134    4177052
    7      859234    3522738
    8      752314    3400604
    9      365314    2541370
   10      265314    1789056
   11      563114    1423742
   12      595314    1158428
 
12 rows selected
     很明显这个语句错了。实际1在这里不是从第1条记录开始的意思,而是指当前记录的前一条记录。preceding前面的修饰符是告诉窗口函数执行时参考的记录数,如同unbounded就是告诉oracle不管当前记录是第几条,只要前面有多少条记录,都列入统计的范围。

三、窗口函数进阶--滚动统计(累积/均值)

     考虑前面提到的第2个需求:列出每月的订单总额以及截至到当前月的订单总额。也就是说2月份的记录要显示当月的订单总额和1,2月份订单总额的和。3月份要显示当月的订单总额和1,2,3月份订单总额的和,依此类推。

    很明显这个需求需要在统计第N月的订单总额时,还要再统计这N个月来的订单总额之和。想想上面的语句,假如我们能够把and unbounded following换成代表当前月份的逻辑多好啊!很幸运的是Oracle考虑到了我们这个需求,为此我们只需要将语句稍微改成: current row就可以了。

SQL> select month,
            sum(total_sales) month_sales,
            sum(sum(total_sales)) over (order by month
                    rows between unbounded preceding and current row ) total_sale
         from win_order group by month;    

MONTH MONTH_SALES TOTAL_SALE
----- ----------- ----------
    1      623141     623141
    2      423124    1046265
    3      323214    1369479
    4      212314    1581793
    5      654314    2236107
    6      122134    2358241
    7      859234    3217475
    8      752314    3969789
    9      365314    4335103
   10      265314    4600417
   11      563114    5163531
   12      595314    5758845
 
12 rows selected

下面的结果一样:

SQL> select month,
  2  sum(total_sales) month_sales,
  3  sum(sum(total_sales)) over (order by month) total_sale
  4  from win_order group by month
  5  ;
 
MONTH MONTH_SALES TOTAL_SALE
----- ----------- ----------
    1      623141     623141
    2      423124    1046265
    3      323214    1369479
    4      212314    1581793
    5      654314    2236107
    6      122134    2358241
    7      859234    3217475
    8      752314    3969789
    9      365314    4335103
   10      265314    4600417
   11      563114    5163531
   12      595314    5758845
 
12 rows selected

因此,rows between unbounded preceding and current row 为窗口默认值!

   现在我们能得到滚动的销售总额了!下面这个统计结果看起来更加完美,它展现了所有我们需要的数据

SQL> select month,
             sum(total_sales) month_sales,
             sum(sum(total_sales)) over(order by month
                rows between unbounded preceding and current row) total_sales,
             sum(sum(total_sales)) over(order by month
             rows between unbounded preceding and unbounded following) total_sales
         from win_order group by month
  8  ;
 
MONTH MONTH_SALES TOTAL_SALES TOTAL_SALES
----- ----------- ----------- -----------
    1      623141      623141     5758845
    2      423124     1046265     5758845
    3      323214     1369479     5758845
    4      212314     1581793     5758845
    5      654314     2236107     5758845
    6      122134     2358241     5758845
    7      859234     3217475     5758845
    8      752314     3969789     5758845
    9      365314     4335103     5758845
   10      265314     4600417     5758845
   11      563114     5163531     5758845
   12      595314     5758845     5758845
 
12 rows selected

在一些销售报表中我们会时常看到求平均值的需求,有时可能是针对全年的数据求平均值,有时会是针对截至到当前的所有数据求平均值。很简单,只需要将:
sum(sum(tot_sales))换成avg(sum(tot_sales))即可。

SQL> select month,
  2         avg(total_sales) month_avg_sales,
  3         avg(avg(total_sales)) over(order by month
  4         rows between unbounded preceding and current row) current_avg_sales,
  5         avg(avg(total_sales)) over(order by month
  6         rows between unbounded preceding and unbounded following) avg_sale
  7  from win_order
  8  group by month
  9  ;
 
MONTH MONTH_AVG_SALES CURRENT_AVG_SALES   AVG_SALE
----- --------------- ----------------- ----------
    1          623141            623141      479903.75
    2          423124          523132.5     479903.75
    3          323214            456493      479903.75
    4          212314         395448.25     479903.75
    5          654314          447221.4      479903.75
    6          122134  393040.166666667  479903.75
    7          859234  459639.285714286  479903.75
    8          752314        496223.625       479903.75
    9          365314  481678.111111111   479903.75
   10          265314          460041.7        479903.75
   11          563114  469411.909090909  479903.75
   12          595314         479903.75       479903.75
 

以下内容转载

四、窗口函数进阶-根据时间范围统计:

前面我们说过,窗口函数不单适用于指定记录集进行统计,而且也能适用于指定范围进行统计的情况,例如下面这个SQL语句就统计了当天销售额和五天内的评价销售额:
  select trunc(order_dt) day,
              sum(sale_price) daily_sales,
              avg(sum(sale_price)) over (order by trunc(order_dt)
                       range between interval '2' day preceding
                                      and interval '2' day following) five_day_avg
    from cust_order
  where sale_price is not null
      and order_dt between to_date('01-jul-2001','dd-mon-yyyy')
      and to_date('31-jul-2001','dd-mon-yyyy')
为了对指定范围进行统计,Oracle使用关键字range、interval来指定一个范围。上面的例子告诉Oracle查找当前日期的前2天,后2天范围内的记录,并统计其销售平均值。

五、窗口函数进阶-first_value/last_value:

Oracle提供了2个额外的函数:first_value、last_value,用于在窗口记录集中查找第一条记录和最后一条记录。假设我们的报表需要显示当前月、上一个月、后一个月的销售情况,以及每3个月的销售平均值,这两个函数就可以派上用场了。
 select month,
              first_value(sum(tot_sales)) over (order by month
                                     rows between 1 preceding and 1 following) prev_month,
 
              sum(tot_sales) monthly_sales,
 
              last_value(sum(tot_sales)) over (order by month
                                   rows between 1 preceding and 1 following) next_month,
 
              avg(sum(tot_sales)) over (order by month
                                  rows between 1 preceding and 1 following) rolling_avg
     from orders
  where year = 2001
       and region_id = 6
   group by month
  order by month;
首先我们来看:rows between 1 preceding and 1 following告诉Oracle在当前记录的前一条、后一条范围内查找并统计,而first_value和last_value在这3条记录中至分别找出第一条、第三条记录,这样我们就轻松地得到相邻三个月的销售记录及平均值了!

六、窗口函数进阶-比较相邻记录:

通过第五部分的学习,我们知道了如何利用窗口函数来显示相邻的记录,现在假如我们想每次显示当月的销售额和上个月的销售额,应该怎么做呢?

从第五部分的介绍我们可以知道,利用first_value(sum(tot_sales) over (order by month rows between 1 preceding and 0 following))就可以做到了,其实Oracle还有一个更简单的方式让我们来比较2条记录,它就是lag函数。

leg函数类似于preceding和following子句,它能够通过和当前记录的相对位置而被应用,在比较同一个相邻的记录集内两条相邻记录的时候特别有用。
 select  month,           
           sum(tot_sales) monthly_sales,
           lag(sum(tot_sales), 1) over (order by month) prev_month_sales
    from orders
  where year = 2001
       and region_id = 6
   group by month
  order by month;
lag(sum(tot_sales),1)中的1表示以1月为基准。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值