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月爲基準。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章