窗口函數--配合over(...)的選項 rows/range between ... preceding and ... following--工作備忘2016/9/30



說明:在使用over()函數進行統計(尤其是求和,求平均的等)的時候,有時候需要設定一個範圍(時間,數值等),因此,oracle提供了窗口函數選項

rows/rang between ... preceding and ... following 


分析函數的語法結構一般是:分析函數名(參數) OVER (PARTITION BY子句 ORDER BY子句 ROWS/RANGE子句)

即由以下三部分組成:
分析函數名:如sum、max、min、count、avg等聚集函數以及lead、lag行比較函數等;
over: 關鍵字,表示前面的函數是分析函數,不是普通的集合函數;
分析子句:over關鍵字後面掛號內的內容;

分析子句又由下面三部分組成:
partition by :分組子句,表示分析函數的計算範圍,不同的組互不相干;
ORDER BY: 排序子句,表示分組後,組內的排序方式;
ROWS/RANGE:窗口子句,是在分組(PARTITION BY)後,組內的子分組(也稱窗口),此時分析函數的計算範圍窗口,而不是PARTITON。窗口有兩種,ROWS和RANGE;
下面分析rows與range窗口子句的用法,先看下面例子:

使用場景:

  1 WITH t AS
  2 (SELECT (CASE
  3 WHEN LEVEL IN (1, 2) THEN
  4 1
  5 WHEN LEVEL IN (4, 5) THEN
  6 6
  7 ELSE
  8 LEVEL
  9 END) ID
 10 FROM dual
 11 CONNECT BY LEVEL < 10)
 12 SELECT id,
 13 SUM(ID) over(ORDER BY ID) default_sum,
 14 SUM(ID) over(ORDER BY ID RANGE BETWEEN unbounded preceding AND CURRENT ROW) range_unbound_sum,
 15 SUM(ID) over(ORDER BY ID ROWS BETWEEN unbounded preceding AND CURRENT ROW) rows_unbound_sum,
 16 SUM(ID) over(ORDER BY ID RANGE BETWEEN 1 preceding AND 2 following) range_sum,
 17 SUM(ID) over(ORDER BY ID ROWS BETWEEN 1 preceding AND 2 following) rows_sum
 18* FROM t
SQL> /

        ID DEFAULT_SUM RANGE_UNBOUND_SUM ROWS_UNBOUND_SUM RANGE_SUM ROWS_SUM
---------- ----------- ----------------- ---------------- ---------- ----------

         1 2           2                 1                5         5
         1 2           2                 2                5         11
         3 5           5                 5                3         16
         6 23          23                11               33        21
         6 23          23                17               33        25
         6 23          23                23               33        27
         7 30          30                30               42        30
         8 38          38                38               24        24
         9 47          47                47               17        17

已選擇9行。

從上面的例子可知:
1、窗口子句必須和order by 子句同時使用,且如果指定了order by 子句未指定窗口子句,則默認爲RANGE BETWEEN unbounded preceding AND CURRENT ROW
,如上例結果集中的defult_sum等於range_unbound_sum
2、如果分析函數沒有指定ORDER BY子句,也就不存在ROWS/RANGE窗口的計算;
3、range是邏輯口,是指定當前行對應的範圍取值,列數不固定,只要行值在範圍內,對應列都包含在內如上例中range_sum(即range 1 preceing and 2 following)例的分析結果:
當id=1時,是sum爲1-1<=id<=1+2 的和,即sum=1+1+3=5(取id爲1,1,3);
當id=3時,是sum爲3-1<=id<=3+2 的和,即sum=3(取id爲3);
當id=6時,是sum爲6-1<=id<=6+2 的和,即sum=6+6+6+7+8=33(取id爲6,6,6,7,8);
以此類推下去,結果如上例中所示。
4、rows是物理窗口,即根據order by 子句排序後,取的前N行及後N行的數據計算(與當前行的值無關,只與排序後的行號相關),如上例中rows_sum例結果,是取前1行和後2行數據的求和,分析上例rows_sum的結果:
當id=1(第一個1時)時,前一行沒數,後二行分別是1和3,sum=1+1+3=5;
當id=3時,前一行id=1,後二行id都爲6,則sum=1+3+6+6=16;
以此類推下去,結果如上例所示。
注:行比較分析函數lead和lag無window(窗口)子句。

實例:


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行。

在一些銷售報表中我們會時常看到求平均值的需求,有時可能是針對全年的數據求平均值,有時會是針對截至到當前的所有數據求平均值。很簡單,只需要將:
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天範圍內的記錄,並統計其銷售平均值。


References:

 http:  //www.cnblogs.com/zf2011/archive/2012/07/04/2576470.html  分析函數全說明

http://blog.csdn.net/rfb0204421/article/details/7675877;

http://blog.csdn.net/rfb0204421/article/details/7673755;

http://www.ecdoer.com/post/oracle-rank-rows.html;








***************************************************************

分析函數補充;  http://blog.csdn.net/clark_xu/article/details/51852277;


/************************ 
*標題:分析函數analytic functions 
*時間:2016-07-07 
*作者:clark 
*************************
分析函數:基於一組記錄(行),計算聚合之的函數; 
和集合函數的區別: 
(1)對於每個分組,返回多個行,而不是一個行; 
(2)分析的一組記錄,稱爲窗口 
(3)每一行,都有一個滑動的窗口; 
(4)計算當前行的時候,窗口決定了完成這個計算的行的範圍; 
(5)在查詢語句中,分析函數是最後的操作;即,它在where ,group by 
order by ,having之後執行; 
(6)因此,分析函數只能出現在select list或者 order by 語句中; 
(7)分析函數一般用於計算累加,移動,集中或者報告總量

–analytic_function::= 
analytic_function ( aruments ) over ( analytic_clause)

–analytic_clause::= 
query_partition_clause order_by_clause windowing_clause 
–注:表明分析函數作用的一個查詢的結果集,它放在from,where group by,having字句之後

–query_partition_clause::= 
partition by (expr) 
–注:partition by 字句;根據一個或者多個expr將結果集(分析函數作用的一個查詢的結果集)分割成組;

–order_by_clause::= 
order siblings by (expr|position|c_alias) (asc|desc) nulls (first|last) 
–注:order_by字句指定了一個數據分區中數據排序; 
–(1)使用rank排列值得實惠,order by多個鍵值特別有用,第二個表達式可以解決第一個表達式相同的值 
–注:order by a,b,c 
–函數表現爲:cume_dist,dense_rank,ntile,percent_rank,rank每一列返回相同的結果 
–row_number分配沒有給列一個不同的值; 
–對於其他解析函數,結果以來窗口;(邏輯窗口,物理窗口)

/*=================================================== 
windows_clause:重點,難點 
=====================================================*/

–windowing_clause::=

(rows|range) between (unbounded preceding|current row|value_expr preceding/following) and (unbounded following|current row|value_expr preceding/following)

(rows|range) (unbounded preceding|current row|value_expr preceding)


rows,指定窗口爲物理行 
range,指定窗口爲邏輯的偏移量 
–窗口的移動從上往下 
–order by 返回的


–between and 
指定窗口的開始點和結束點


–unbounded preceding:指定的窗口從第一個分區開始;是起點規範 
–unbounded following:指定窗口結束在分區的最後一行,是終點規範 
–value_expr preceding/following


current row 
–注:指定了窗口開始在當前行;


value_expr preceding/following 
–注:value_expr preceding開始,必須value_expr preceding結束; 
–value_expr following 開始,必須value_expr following 結束;


rows value_expr preceding/following 
–注: 
value_expr 是物理偏移量,它必須是正確的數字或者表達式 
value_expr是起點,必須是終點的前一行;


range value_expr preceding/following 
–注: 
–如果value_expr是數值,order by 必須是數值 
–如果value_expr是區間值,order by 必須是日期數據類型 
–如果忽略windows_clause;則窗口是unbounded preceding and current row;

/************************ 
*標題:avg函數 
*時間:2016-07-07 
*作者:clark 
*************************
–(1)聚合aggregate 
select avg(out_row) from j1_dw.etl_exdw_log 
–(2)分析例子 
/*————————————— 
****rows bwteen 
(1)unbounded PRECEDING AND current row 
(2)nbounded PRECEDING AND unbounded following 
(3)無windows_clause 等同於nbounded PRECEDING AND unbounded following 
(4)1 preceding and 1 following 
—————————————–*/ 
–ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 
SELECT unit_id, 
unit_code, 
tjrq_q, 
out_row, 
AVG(out_row) OVER(PARTITION BY unit_id, tjrq_q ORDER BY tjrq_q /ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING/) AS c_mavg 
FROM etl_exdw_log 
ORDER BY unit_id, unit_code, tjrq_q, out_row

–rows BETWEEN unbounded PRECEDING AND current row 
SELECT unit_id, 
unit_code, 
tjrq_q, 
out_row, 
AVG(out_row) OVER(PARTITION BY unit_id, tjrq_q ORDER BY tjrq_q rows BETWEEN unbounded PRECEDING AND current row) AS c_mavg 
FROM etl_exdw_log 
ORDER BY unit_id, unit_code, tjrq_q, out_row

–rows BETWEEN unbounded PRECEDING AND unbounded following 
SELECT unit_id, 
unit_code, 
tjrq_q, 
out_row, 
AVG(out_row) OVER(PARTITION BY unit_id,tjrq_q ORDER BY tjrq_q rows BETWEEN unbounded PRECEDING AND unbounded following) AS c_mavg 
FROM etl_exdw_log 
ORDER BY unit_id, unit_code, tjrq_q, out_row

/*—————————————

** rows unbounded preceding|current row|value_expr preceding

—————————————–*/ 
–rows unbounded PRECEDING 
–等同於rows BETWEEN unbounded PRECEDING AND current row 
SELECT unit_id, 
unit_code, 
tjrq_q, 
out_row, 
AVG(out_row) OVER(PARTITION BY unit_id, tjrq_q ORDER BY tjrq_q rows unbounded PRECEDING) AS c_mavg 
FROM etl_exdw_log 
ORDER BY unit_id, unit_code, tjrq_q, out_row

–rows 1 preceding 
–當前行,和前一行做分析 
–rows current row 
–不做分析,和原值相同 
SELECT unit_id, 
unit_code, 
tjrq_q, 
out_row, 
AVG(out_row) OVER(PARTITION BY unit_id, tjrq_q ORDER BY tjrq_q rows 1 preceding) AS c_mavg 
FROM etl_exdw_log 
ORDER BY unit_id, unit_code, tjrq_q, out_row


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