視窗定義:内置使用 OVER 子句和 WINDOW 子句
定義視窗的方法有三種:
- 内置(在視窗函數的 OVER 子句中)
- 在 WINDOW 子句中
- 部分内置,部分在 WINDOW 子句中
然而,有些方法存在限制,如以下各節中所述。
内置定義(在視窗函數的 OVER 子句中)
視窗定義可以放在視窗函數的 OVER 子句中。這稱為以内置 方式定義視窗。
例如,以下語句在示例資料庫中查詢 2001 年 7 月和 8 月發運的所有産品,以及截至發運日期的累計發運量。視窗采用内置方式定義。
|
此查詢會傳回以下結果:
ID | Description | Quantity | ShipDate | Cumulative_qty | |
---|---|---|---|---|---|
1 | 301 | V-neck | 24 | 2001-07-16 | 24 |
2 | 302 | Crew Neck | 60 | 2001-07-02 | 60 |
3 | 302 | Crew Neck | 36 | 2001-07-13 | 96 |
4 | 400 | Cotton Cap | 48 | 2001-07-05 | 48 |
5 | 400 | Cotton Cap | 24 | 2001-07-19 | 72 |
6 | 401 | Wool Cap | 48 | 2001-07-09 | 48 |
7 | 500 | Cloth Visor | 12 | 2001-07-22 | 12 |
8 | 501 | Plastic Visor | 60 | 2001-07-07 | 60 |
9 | 501 | Plastic Visor | 12 | 2001-07-12 | 72 |
10 | 501 | Plastic Visor | 12 | 2001-07-22 | 84 |
11 | 601 | Zipped Sweatshirt | 60 | 2001-07-19 | 60 |
12 | 700 | Cotton Shorts | 24 | 2001-07-26 | 24 |
在此示例中,要在連接配接兩個表和應用查詢的 WHERE 子句之後,才執行 SUM 視窗函數的計算。查詢會按如下方式進行:
- 根據值 ProductID 分區(分組)輸入行。
- 在每個分區内,根據 ShipDate 的值對行進行排序。
- 對于分區中的每一行,通過由各分區中(經過排序的)第一行直到目前行并包括目前行所組成的滑動視窗,使用 SUM 函數對 Quantity 中的值進行求值。
WINDOW 子句定義
上述查詢的另一種結構是,使用 WINDOW 子句在使用視窗的函數中單獨指定視窗,然後在各函數的 OVER 子句内引用視窗。
在此示例中,WINDOW 子句建立名為 Cumulative、按 ProductID 對資料分區并按 ShipDate 進行排序的視窗。SUM 函數在其 OVER 子句中引用視窗,并使用 ROWS 子句定義視窗大小。
|
當使用 WINDOW 子句文法時,以下限制将适用:
- 如果指定 PARTITION BY 子句,則必須将其置于 WINDOW 子句内。
- 如果指定 ROWS 或 RANGE 子句,則必須将其置于引用函數的 OVER 子句中。
- 如果為視窗指定 ORDER BY 子句,可将該子句置于 WINDOW 子句中,或者置于引用函數的 OVER 子句中,但不能同時置于二者之中。
- WINDOW 子句必須位于 SELECT 語句的 ORDER BY 子句之前。
内置與 WINDOW 子句定義相結合
可以内置一部分視窗定義,然後在 WINDOW 子句中定義剩餘部分。例如:
|
使用此方式分隔視窗定義時有以下限制:
- 不能在視窗函數文法中使用 PARTITION BY 子句。
- 可以在視窗函數文法或 WINDOW 子句中使用 ORDER BY 子句,但不能在二者中同時使用。
-
不能在 WINDOW 子句中包括 RANGE 或 ROWS 子句。
-------------------------------------------------------------------------------------------------------------------------------
一、Oracle分析函數入門
分析函數是什麼?
分析函數是Oracle專門用于解決複雜報表統計需求的功能強大的函數,它可以在資料中進行分組然後計算基于組的某種統計值,并且每一組的每一行都可以傳回一個統計值。
分析函數和聚合函數的不同之處是什麼?
普通的聚合函數用group by分組,每個分組傳回一個統計值,而分析函數采用partition by分組,并且每組每行都可以傳回一個統計值。
分析函數的形式
分析函數帶有一個開窗函數over(),包含三個分析子句:分組(partition by), 排序(order by), 視窗(rows) ,他們的使用形式如下:over(partition by xxx order by yyy rows between zzz)。
注:視窗子句在這裡我隻說rows方式的視窗,range方式和滑動視窗也不提
分析函數例子(在scott使用者下模拟)
示例目的:顯示各部門員工的工資,并附帶顯示該部分的最高工資。
運作結果: 示例目的:按照deptno分組,然後計算每組值的總和--顯示各部門員工的工資,并附帶顯示該部分的最高工資。 SELECT E.DEPTNO, E.EMPNO, E.ENAME, E.SAL, LAST_VALUE(E.SAL) OVER(PARTITION BY E.DEPTNO ORDER BY E.SAL ROWS --unbounded preceding and unbouned following針對目前所有記錄的前一條、後一條記錄,也就是表中的所有記錄 --unbounded:不受控制的,無限的 --preceding:在...之前 --following:在...之後 BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) MAX_SAL FROM EMP E;
運作結果: 示例目的:對各部門進行分組,并附帶顯示第一行至目前行的彙總SELECT EMPNO, ENAME, DEPTNO, SAL, SUM(SAL) OVER(PARTITION BY DEPTNO ORDER BY ENAME) max_sal FROM SCOTT.EMP;
運作結果: 示例目标:目前行至最後一行的彙總SELECT EMPNO, ENAME, DEPTNO, SAL, --注意ROWS BETWEEN unbounded preceding AND current row 是指第一行至目前行的彙總 SUM(SAL) OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_sal FROM SCOTT.EMP;
運作結果: 示例目标:目前行的上一行(rownum-1)到目前行的彙總SELECT EMPNO, ENAME, DEPTNO, SAL, --注意ROWS BETWEEN current row AND unbounded following 指目前行到最後一行的彙總 SUM(SAL) OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) max_sal FROM SCOTT.EMP;
運作結果: 示例目标: 目前行的上一行(rownum-1)到目前行的下輛行(rownum+2)的彙總SELECT EMPNO, ENAME, DEPTNO, SAL, --注意ROWS BETWEEN 1 preceding AND current row 是指目前行的上一行(rownum-1)到目前行的彙總 SUM(SAL) OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) max_sal FROM SCOTT.EMP;
運作結果:SELECT EMPNO, ENAME, DEPTNO, SAL, --注意ROWS BETWEEN 1 preceding AND 1 following 是指目前行的上一行(rownum-1)到目前行的下輛行(rownum+2)的彙總 SUM(SAL) OVER(PARTITION BY DEPTNO ORDER BY ENAME ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING) max_sal FROM SCOTT.EMP;
______________________________________________________________________________________________________________________
http://www.cnblogs.com/wuyisky/archive/2010/06/03/1750935.html
oracle 分析函數over
分析函數(OVER)
目錄:
===============================================
1.Oracle分析函數簡介
2. Oracle分析函數簡單執行個體
3.分析函數OVER解析
一、Oracle分析函數簡介:
在日常的生産環境中,我們接觸得比較多的是OLTP系統(即Online Transaction Process),這些系統的特點是具備實時要求,或者至少說對響應的時間多長有一定的要求;其次這些系統的業務邏輯一般比較複雜,可能需要經過多次的運算。比如我們經常接觸到的電子商城。
在這些系統之外,還有一種稱之為OLAP的系統(即Online Aanalyse Process),這些系統一般用于系統決策使用。通常和資料倉庫、資料分析、資料挖掘等概念聯系在一起。這些系統的特點是資料量大,對實時響應的要求不高或者根本不關注這方面的要求,以查詢、統計操作為主。
我們來看看下面的幾個典型例子:
①查找上一年度各個銷售區域排名前10的員工
②按區域查找上一年度訂單總額占區域訂單總額20%以上的客戶
③查找上一年度銷售最差的部門所在的區域
④查找上一年度銷售最好和最差的産品
我們看看上面的幾個例子就可以感覺到這幾個查詢和我們日常遇到的查詢有些不同,具體有:
①需要對同樣的資料進行不同級别的聚合操作
②需要在表内将多條資料和同一條資料進行多次的比較
③需要在排序完的結果集上進行額外的過濾操作
分析函數文法:
FUNCTION_NAME(<argument>,<argument>...)
OVER
(<Partition-Clause><Order-by-Clause><Windowing Clause>)
例:
sum(sal) over (partition by deptno order by ename) new_alias
sum就是函數名
(sal)是分析函數的參數,每個函數有0~3個參數,參數可以是表達式,例如:sum(sal+comm)
over 是一個關鍵字,用于辨別分析函數,否則查詢分析器不能差別sum()聚集函數和sum()分析函數
partition by deptno 是可選的分區子句,如果不存在任何分區子句,則全部的結果集可看作一個單一的大區
order by ename 是可選的order by 子句,有些函數需要它,有些則不需要.依靠已排序資料的那些函數,如:用于通路結果集中前一行和後一行的LAG和LEAD,必須使用,其它函數,如AVG,則不需要.在使用了任何排序的開窗函數時,該子句是強制性的,它指定了在計算分析函數時一組内的資料是如何排序的.
1)FUNCTION子句
ORACLE提供了26個分析函數,按功能分5類
分析函數分類
等級(ranking)函數:用于尋找前N種查詢
開窗(windowing)函數:用于計算不同的累計,如SUM,COUNT,AVG,MIN,MAX等,作用于資料的一個視窗上
例:
sum(t.sal) over (order by t.deptno,t.ename) running_total,
sum(t.sal) over (partition by t.deptno order by t.ename) department_total
制表(reporting)函數:與開窗函數同名,作用于一個分區或一組上的所有列
例:
sum(t.sal) over () running_total2,
sum(t.sal) over (partition by t.deptno ) department_total2
制表函數與開窗函數的關鍵不同之處在于OVER語句上缺少一個ORDER BY子句!
LAG,LEAD函數:這類函數允許在結果集中向前或向後檢索值,為了避免資料的自連接配接,它們是非常用用的.
VAR_POP,VAR_SAMP,STDEV_POPE及線性的衰減函數:計算任何未排序分區的統計值
2)PARTITION子句
按照表達式分區(就是分組),如果省略了分區子句,則全部的結果集被看作是一個單一的組
3)ORDER BY子句
分析函數中ORDER BY的存在将添加一個預設的開窗子句,這意味着計算中所使用的行的集合是目前分區中目前行和前面所有行,沒有ORDER BY時,預設的視窗是全部的分區 在Order by 子句後可以添加nulls last,如:order by comm desc nulls last 表示排序時忽略comm列為空的行.
4)WINDOWING子句
用于定義分析函數将在其上操作的行的集合
Windowing子句給出了一個定義變化或固定的資料視窗的方法,分析函數将對這些資料進行操作
預設的視窗是一個固定的視窗,僅僅在一組的第一行開始,一直繼續到目前行,要使用視窗,必須使用ORDER BY子句
根據2個标準可以建立視窗:資料值的範圍(RANGES)或與目前行的行偏移量.
5)Rang視窗
Range 5 preceding:将産生一個滑動視窗,他在組中擁有目前行以前5行的集合
ANGE視窗僅對NUMBERS和DATES起作用,因為不可能從VARCHAR2中增加或減去N個單元
另外的限制是ORDER BY中隻能有一列,因而範圍實際上是一維的,不能在N維空間中
例:
avg(t.sal) over(order by t.hiredate asc range 100 preceding) 統計前100天平均工資
6)Row視窗
利用ROW分區,就沒有RANGE分區那樣的限制了,資料可以是任何類型,且ORDER BY 可以包括很多列
7)Specifying視窗
UNBOUNDED PRECEDING:這個視窗從目前分區的每一行開始,并結束于正在處理的目前行
CURRENT ROW:該視窗從目前行開始(并結束)
Numeric Expression PRECEDING:對該視窗從目前行之前的數字表達式(Numeric Expression)的行開始,對RANGE來說,從從行序值小于數字表達式的目前行的值開始.
Numeric Expression FOLLOWING:該視窗在目前行Numeric Expression行之後的行終止(或開始),且從行序值大于目前行Numeric Expression行的範圍開始(或終止)
range between 100 preceding and 100 following:目前行100前,目前後100後
注意:分析函數允許你對一個資料集進排序和篩選,這是SQL從來不能實作的.除了最後的Order by子句之外,分析函數是在查詢中執行的最後的操作集,這樣的話,就不能直接在謂詞中使用分析函數,即不能在上面使用where或having子句!!!
二、Oracle分析函數簡單執行個體:
下面我們通過一個實際的例子:按區域查找上一年度訂單總額占區域訂單總額20%以上的客戶,來看看分析函數的應用。
【1】測試環境:
SQL> desc orders_tmp;
Name Null? Type
----------------------- -------- ----------------
CUST_NBR NOT NULL NUMBER(5)
REGION_ID NOT NULL NUMBER(5)
SALESPERSON_ID NOT NULL NUMBER(5)
YEAR NOT NULL NUMBER(4)
MONTH NOT NULL NUMBER(2)
TOT_ORDERS NOT NULL NUMBER(7)
TOT_SALES NOT NULL NUMBER(11,2)
【2】測試資料:
SQL> select * from orders_tmp;
CUST_NBR REGION_ID SALESPERSON_ID YEAR MONTH TOT_ORDERS TOT_SALES
---------- ---------- -------------- ---------- ---------- ---------- ----------
11 7 11 2001 7 2 12204
4 5 4 2001 10 2 37802
7 6 7 2001 2 3 3750
10 6 8 2001 1 2 21691
10 6 7 2001 2 3 42624
15 7 12 2000 5 6 24
12 7 9 2000 6 2 50658
1 5 2 2000 3 2 44494
1 5 1 2000 9 2 74864
2 5 4 2000 3 2 35060
2 5 4 2000 4 4 6454
2 5 1 2000 10 4 35580
4 5 4 2000 12 2 39190
13 rows selected.
【3】測試語句:
SQL> select o.cust_nbr customer,
2 o.region_id region,
3 sum(o.tot_sales) cust_sales,
4 sum(sum(o.tot_sales)) over(partition by o.region_id) region_sales
5 from orders_tmp o
6 where o.year = 2001
7 group by o.region_id, o.cust_nbr;
CUSTOMER REGION CUST_SALES REGION_SALES
---------- ---------- ---------- ------------
4 5 37802 37802
7 6 3750 68065
10 6 64315 68065
11 7 12204 12204
三、分析函數OVER解析:
請注意上面的綠色高亮部分,group by的意圖很明顯:将資料按區域ID,客戶進行分組,那麼Over這一部分有什麼用呢?假如我們隻需要統計每個區域每個客戶的訂單總額,那麼我們隻需要group by o.region_id,o.cust_nbr就夠了。但我們還想在每一行顯示該客戶所在區域的訂單總額,這一點和前面的不同:需要在前面分組的基礎上按區域累加。很顯然group by和sum是無法做到這一點的(因為聚集操作的級别不一樣,前者是對一個客戶,後者是對一批客戶)。
這就是over函數的作用了!它的作用是告訴SQL引擎:按區域對資料進行分區,然後累積每個區域每個客戶的訂單總額(sum(sum(o.tot_sales)))。
現在我們已經知道2001年度每個客戶及其對應區域的訂單總額,那麼下面就是篩選那些個人訂單總額占到區域訂單總額20%以上的大客戶了
SQL> select *
2 from (select o.cust_nbr customer,
3 o.region_id region,
4 sum(o.tot_sales) cust_sales,
5 sum(sum(o.tot_sales)) over(partition by o.region_id) region_sales
6 from orders_tmp o
7 where o.year = 2001
8 group by o.region_id, o.cust_nbr) all_sales
9 where all_sales.cust_sales > all_sales.region_sales * 0.2;
CUSTOMER REGION CUST_SALES REGION_SALES
---------- ---------- ---------- ------------
4 5 37802 37802
10 6 64315 68065
11 7 12204 12204
SQL>
現在我們已經知道這些大客戶是誰了!哦,不過這還不夠,如果我們想要知道每個大客戶所占的訂單比例呢?看看下面的SQL語句,隻需要一個簡單的Round函數就搞定了。
SQL> select all_sales.*,
2 100 * round(cust_sales / region_sales, 2) || '%' Percent
3 from (select o.cust_nbr customer,
4 o.region_id region,
5 sum(o.tot_sales) cust_sales,
6 sum(sum(o.tot_sales)) over(partition by o.region_id) region_sales
7 from orders_tmp o
8 where o.year = 2001
9 group by o.region_id, o.cust_nbr) all_sales
10 where all_sales.cust_sales > all_sales.region_sales * 0.2;
CUSTOMER REGION CUST_SALES REGION_SALES PERCENT
---------- ---------- ---------- ------------ ----------------------------------------
4 5 37802 37802 100%
10 6 64315 68065 94%
11 7 12204 12204 100%
SQL>
總結:
①Over函數指明在那些字段上做分析,其内跟Partition by表示對資料進行分組。注意Partition by可以有多個字段。
②Over函數可以和其它聚集函數、分析函數搭配,起到不同的作用。例如這裡的SUM,還有諸如Rank,Dense_rank
oracle分析函數Rank, Dense_rank, row_number
分析函數2(Rank, Dense_rank, row_number)
目錄
===============================================
1.使用rownum為記錄排名
2.使用分析函數來為記錄排名
3.使用分析函數為記錄進行分組排名
一、使用rownum為記錄排名:
在前面一篇《Oracle開發專題之:分析函數》,我們認識了分析函數的基本應用,現在我們再來考慮下面幾個問題:
①對所有客戶按訂單總額進行排名
②按區域和客戶訂單總額進行排名
③找出訂單總額排名前13位的客戶
④找出訂單總額最高、最低的客戶
⑤找出訂單總額排名前25%的客戶
按照前面第一篇文章的思路,我們隻能做到對各個分組的資料進行統計,如果需要排名的話那麼隻需要簡單地加上rownum不就行了嗎?事實情況是否如此想象般簡單,我們來實踐一下。
【1】測試環境:
SQL> desc user_order;
Name Null? Type
----------------------------------------- -------- ----------------------------
REGION_ID NUMBER(2)
CUSTOMER_ID NUMBER(2)
CUSTOMER_SALES NUMBER
【2】測試資料:
SQL> select * from user_order order by customer_sales;
REGION_ID CUSTOMER_ID CUSTOMER_SALES
---------- ----------- --------------
5 1 151162
10 29 903383
6 7 971585
10 28 986964
9 21 1020541
9 22 1036146
8 16 1068467
6 8 1141638
5 3 1161286
5 5 1169926
8 19 1174421
7 12 1182275
7 11 1190421
6 10 1196748
6 9 1208959
10 30 1216858
5 2 1224992
9 24 1224992
9 23 1224992
8 18 1253840
7 15 1255591
7 13 1310434
10 27 1322747
8 20 1413722
6 6 1788836
10 26 1808949
5 4 1878275
7 14 1929774
8 17 1944281
9 25 2232703
30 rows selected.
注意這裡有3條記錄的訂單總額是一樣的。假如我們現在需要篩選排名前12位的客戶,如果使用rownum會有什麼樣的後果呢?
SQL> select rownum, t.*
2 from (select *
3 from user_order
4 order by customer_sales desc) t
5 where rownum <= 12
6 order by customer_sales desc;
ROWNUM REGION_ID CUSTOMER_ID CUSTOMER_SALES
---------- ---------- ----------- --------------
1 9 25 2232703
2 8 17 1944281
3 7 14 1929774
4 5 4 1878275
5 10 26 1808949
6 6 6 1788836
7 8 20 1413722
8 10 27 1322747
9 7 13 1310434
10 7 15 1255591
11 8 18 1253840
12 5 2 1224992
12 rows selected.
很明顯假如隻是簡單地按rownum進行排序的話,我們漏掉了另外兩條記錄(參考上面的結果)。
二、使用分析函數來為記錄排名:
針對上面的情況,Oracle從8i開始就提供了3個分析函數:rand,dense_rank,row_number來解決諸如此類的問題,下面我們來看看這3個分析函數的作用以及彼此之間的差別:
Rank,Dense_rank,Row_number函數為每條記錄産生一個從1開始至N的自然數,N的值可能小于等于記錄的總數。這3個函數的唯一差別在于當碰到相同資料時的排名政策。
①ROW_NUMBER:
Row_number函數傳回一個唯一的值,當碰到相同資料時,排名按照記錄集中記錄的順序依次遞增。
②DENSE_RANK:
Dense_rank函數傳回一個唯一的值,除非當碰到相同資料時,此時所有相同資料的排名都是一樣的。
③RANK:
Rank函數傳回一個唯一的值,除非遇到相同的資料時,此時所有相同資料的排名是一樣的,同時會在最後一條相同記錄和下一條不同記錄的排名之間空出排名。
這樣的介紹有點難懂,我們還是通過執行個體來說明吧,下面的例子示範了3個不同函數在遇到相同資料時不同排名政策:
SQL> select region_id, customer_id, sum(customer_sales) total,
2 rank() over(order by sum(customer_sales) desc) rank,
3 dense_rank() over(order by sum(customer_sales) desc) dense_rank,
4 row_number() over(order by sum(customer_sales) desc) row_number
5 from user_order
6 group by region_id, customer_id;
REGION_ID CUSTOMER_ID TOTAL RANK DENSE_RANK ROW_NUMBER
---------- ----------- ---------- ---------- ---------- ----------
8 18 1253840 11 11 11
5 2 1224992 12 12 12
9 23 1224992 12 12 13
9 24 1224992 12 12 14
10 30 1216858 15 13 15
30 rows selected.
請注意上面的綠色高亮部分,這裡生動的示範了3種不同的排名政策:
①對于第一條相同的記錄,3種函數的排名都是一樣的:12
②當出現第二條相同的記錄時,Rank和Dense_rank依然給出同樣的排名12;而row_number則順延遞增為13,依次類推至第三條相同的記錄
③當排名進行到下一條不同的記錄時,可以看到Rank函數在12和15之間空出了13,14的排名,因為這2個排名實際上已經被第二、三條相同的記錄占了。而Dense_rank則順序遞增。row_number函數也是順序遞增
比較上面3種不同的政策,我們在選擇的時候就要根據客戶的需求來定奪了:
①假如客戶就隻需要指定數目的記錄,那麼采用row_number是最簡單的,但有漏掉的記錄的危險
②假如客戶需要所有達到排名水準的記錄,那麼采用rank或dense_rank是不錯的選擇。至于選擇哪一種則看客戶的需要,選擇dense_rank或得到最大的記錄
三、使用分析函數為記錄進行分組排名:
上面的排名是按訂單總額來進行排列的,現在跟進一步:假如是為各個地區的訂單總額進行排名呢?這意味着又多了一次分組操作:對記錄按地區分組然後進行排名。幸虧Oracle也提供了這樣的支援,我們所要做的僅僅是在over函數中order by的前面增加一個分組子句:partition by region_id。
SQL> select region_id, customer_id,
sum(customer_sales) total,
2 rank() over(partition by region_id
order by sum(customer_sales) desc) rank,
3 dense_rank() over(partition by region_id
order by sum(customer_sales) desc) dense_rank,
4 row_number() over(partition by region_id
order by sum(customer_sales) desc) row_number
5 from user_order
6 group by region_id, customer_id;
REGION_ID CUSTOMER_ID TOTAL RANK DENSE_RANK ROW_NUMBER
---------- ----------- ---------- ---------- ---------- ----------
5 4 1878275 1 1 1
5 2 1224992 2 2 2
5 5 1169926 3 3 3
6 6 1788836 1 1 1
6 9 1208959 2 2 2
6 10 1196748 3 3 3
30 rows selected.
現在我們看到的排名将是基于各個地區的,而非所有區域的了! Partition by 子句在排列函數中的作用是将一個結果集劃分成幾個部分,這樣排列函數就能夠應用于這各個子集。
前面我們提到的 5 個問題已經解決了 2 個了 ( 第 1,2) ,剩下的 3 個問題 (Top/Bottom N , First/Last, NTile) 會在下一篇講解。
___
http://blog.csdn.net/sqlserverdiscovery/article/details/50495997