Oracle开发之:窗口函数 (转) rows between unbounded preceding and current row

本文通过具体案例介绍了窗口函数在SQL查询中的应用,包括全统计、滚动统计、时间范围统计等高级用法,展示了如何利用first_value/last_value和lag函数解决复杂统计需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录
=========================================
1.窗口函数简介 
2.窗口函数示例-全统计 
3.窗口函数进阶-滚动统计(累积/均值) 
4.窗口函数进阶-根据时间范围统计 
5.窗口函数进阶-first_value/last_value 
6.窗口函数进阶-比较相邻记录 

一、窗口函数简介:

到目前为止,我们所学习的分析函数在计算/统计一段时间内的数据时特别有用,但是假如计算/统计需要随着遍历记录集的每一条记录而进行呢?举些例子来说:

①列出每月的订单总额以及全年的订单总额
②列出每月的订单总额以及截至到当前月的订单总额
③列出上个月、当月、下一月的订单总额以及全年的订单总额
④列出每天的营业额及一周来的总营业额
⑤列出每天的营业额及一周来每天的平均营业额

仔细回顾一下前面我们介绍到的分析函数,我们会发现这些需求和前面有一些不同:前面我们介绍的分析函数用于计算/统计一个明确的阶段/记录集,而这里有部分需求例如2,需要随着遍历记录集的每一条记录的同时进行统计。

也即是说:统计不止发生一次,而是发生多次。统计不至发生在记录集形成后,而是发生在记录集形成的过程中。

这就是我们这次要介绍的窗口函数的应用了。它适用于以下几个场合:

①通过指定一批记录:例如从当前记录开始直至某个部分的最后一条记录结束
②通过指定一个时间间隔:例如在交易日之前的前30天
③通过指定一个范围值:例如所有占到当前交易量总额5%的记录

二、窗口函数示例-全统计:

下面我们以需求:列出每月的订单总额以及全年的订单总额为例,来看看窗口函数的应用。

【1】测试环境:

SQL  >     desc   orders;
 名称                    是否为空? 类型
 
  --  --------------------- -------- ---------------- 
   MONTH                                NUMBER  (  2  )
 TOT_SALES                    
  NUMBER 

SQL
  >   


【2】测试数据:

SQL  >     select     *     from   orders;

     
  MONTH    TOT_SALES
--  -------- ---------- 
           1         610697 
         
  2         428676 
         
  3         637031 
         
  4         541146 
         
  5         592935 
         
  6         501485 
         
  7         606914 
         
  8         460520 
         
  9         392898 
        
  10         510117 
        
  11         532889 
        
  12         492458 

已选择12行。


【3】测试语句:

回忆一下前面《Oracle开发专题之:分析函数(OVER) 》一文中,我们使用了sum(sum(tot_sales)) over (partition by region_id) 来统计每个分区的订单总额。现在我们要统计的不单是每个分区,而是所有分区,partition by region_id在这里不起作用了。

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

SQL  >     select     month  ,
  
  2             sum  (tot_sales) month_sales,
  
  3             sum  (  sum  (tot_sales))   over   (  order     by     month 
  
  4              rows  between  unbounded preceding  and  unbounded following  ) total_sales
  
  5        from   orders
  
  6       group     by     month  ;

     
  MONTH   MONTH_SALES TOTAL_SALES
--  -------- ----------- ----------- 
           1          610697         6307766 
         
  2          428676         6307766 
         
  3          637031         6307766 
         
  4          541146         6307766 
         
  5          592935         6307766 
         
  6          501485         6307766 
         
  7          606914         6307766 
         
  8          460520         6307766 
         
  9          392898         6307766 
        
  10          510117         6307766 
        
  11          532889         6307766 
        
  12          492458         6307766 

已选择12行。


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

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

SQL  >     select     month  ,
  
  2             sum  (tot_sales) month_sales,
  
  3             sum  (  sum  (tot_sales))   over   (  order     by     month 
  
  4              rows   between     1   preceding   and   unbounded   following) all_sales
  
  5        from   orders
  
  6       group     by     month  ;

     
  MONTH   MONTH_SALES  ALL_SALES
--  -------- ----------- ---------- 
           1          610697        6307766 
         
  2          428676        6307766 
         
  3          637031        5697069 
         
  4          541146        5268393 
         
  5          592935        4631362 
         
  6          501485        4090216 
         
  7          606914        3497281 
         
  8          460520        2995796 
         
  9          392898        2388882 
        
  10          510117        1928362 
        
  11          532889        1535464 
        
  12          492458        1025347 

已选择12行。


很明显这个语句错了。实际1在这里不是从第1条记录开始的意思,而是指当前记录的前一条记录。preceding前面的修饰符是告诉窗口函数执行时参考的记录数,如同unbounded就是告诉oracle不管当前记录是第几条,只要前面有多少条记录,都列入统计的范围。 

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

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

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

SQL  >     select     month  ,
  
  2             sum  (tot_sales) month_sales,
  
  3             sum  (  sum  (tot_sales))   over  (  order     by     month 
  
  4             rows  between  unbounded preceding  and   current  row  ) current_total_sales
  
  5        from   orders
  
  6       group     by     month  ;

     
  MONTH   MONTH_SALES CURRENT_TOTAL_SALES
--  -------- ----------- ------------------- 
           1          610697                  610697 
         
  2          428676                 1039373 
         
  3          637031                 1676404 
         
  4          541146                 2217550 
         
  5          592935                 2810485 
         
  6          501485                 3311970 
         
  7          606914                 3918884 
         
  8          460520                 4379404 
         
  9          392898                 4772302 
        
  10          510117                 5282419 
        
  11          532889                 5815308 
        
  12          492458                 6307766 

已选择12行。


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

SQL  >     select     month  ,
  
  2             sum  (tot_sales) month_sales,
  
  3             sum  (  sum  (tot_sales))   over  (  order     by     month 
  
  4           rows   between   unbounded preceding   and     current  row  ) current_total_sales,
  
  5             sum  (  sum  (tot_sales))   over  (  order     by     month 
  
  6           rows   between   unbounded preceding   and   unbounded following ) total_sales
  
  7        from   orders
  
  8       group     by     month  ;

     
  MONTH   MONTH_SALES CURRENT_TOTAL_SALES TOTAL_SALES
--  -------- ----------- ------------------- ----------- 
           1          610697                  610697         6307766 
         
  2          428676                 1039373         6307766 
         
  3          637031                 1676404         6307766 
         
  4          541146                 2217550         6307766 
         
  5          592935                 2810485         6307766 
         
  6          501485                 3311970         6307766 
         
  7          606914                 3918884         6307766 
         
  8          460520                 4379404         6307766 
         
  9          392898                 4772302         6307766 
        
  10          510117                 5282419         6307766 
        
  11          532889                 5815308         6307766 
        
  12          492458                 6307766         6307766 

已选择12行。

 

 

 


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

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


前面我们说过,窗口函数不单适用于指定记录集进行统计,而且也能适用于指定范围进行统计的情况,例如下面这个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 月为基准。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值