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