1. 程式人生 > >oracle高階分析函式使用例項

oracle高階分析函式使用例項

ORACLE的分析函式,發現大家寫SQL的時候有些功能寫的比較麻煩或者不知道複雜的功能怎麼通過SQL實現,ORACLE自帶的分析函式有很多相應的功能:

它是Oracle分析函式專門針對類似於"經營總額"、"找出一組中的百分之多少" 或"計算排名前幾位"等問題設計的。
分析函式執行效率高,使用方便。

分析函式是基於一組行來計算的。這不同於聚集函式且廣泛應用於OLAP環境中。

Oracle從8.1.6開始提供分析函式,分析函式用於計算基於組的某種聚合值,它和聚合函式的不同之處是
對於每個組返回多行,而聚合函式對於每個組只返回一行。

語法:
<analytic-function>(<argument>,<argument>,...)
over(
<query-partition-clause>
<order-by-clause>
<windowing-clause>
)
其中:
1 over是關鍵字,用於標識分析函式。

2 <analytic-function>是指定的分析函式的名字。Oracle分析函式很多。

3 <argument>為引數,分析函式可以選取0-3個引數。

4 分割槽子句<query-partition-clause>的格式為:
partition by<value_exp>[,value_expr]...
關鍵字partition by子句根據由分割槽表示式的條件邏輯地將單個結果集分成N組。這裡的"分割槽partition"和"組group"
都是同義詞。

5 排序子句order-by-clause指定資料是如何存在分割槽內的。其格式為:
order[siblings]by{expr|position|c_alias}[asc|desc][nulls first|nulls last]
其中:
(1)asc|desc:指定了排列順序。
(2)nulls first|nulls last:指定了包含空值的返回行應出現在有序序列中的第一個或最後一個位置。

6視窗子句windowing-clause
給出一個固定的或變化的資料視窗方法,分析函式將對這些資料進行操作。在一組基於任意變化或固定的視窗中,
可用該子句讓分析函式計算出它的值。
格式:
{rows|range}
{between
{unbounded preceding|current row |<value_expr>{preceding|following}
}and
{unbounded preceding|current row |<value_expr>{preceding|following}
}|{unbounded preceding|current row |<value_expr>{preceding|following
}}
(1)rows|range:此關鍵字定義了一個window。
(2)between...and...:為窗品指一個起點和終點。
(3)unbounded preceding:指明視窗是從分割槽(partition)的第一行開始。
(4)current row:指明視窗是從當前行開始。

create table emp(
deptno varchar2(20),--部門編碼
ename varchar2(20),--人名
sal number(10));--工資

insert into emp values('10','andy1',2000);
insert into emp values('10','andy2',3000);
insert into emp values('10','andy3',2000);
insert into emp values('20','leno1',4000);
insert into emp values('20','leno2',8000);
insert into emp values('20','leno3',6000);
insert into emp values('30','jack1',5000);
insert into emp values('30','jack2',6000);
insert into emp values('30','jack3',7000);

1 連續求和
select deptno,ename,sal,sum(sal) over(order by ename) 連續求和 from emp;

DEPTNO ENAME SAL 連續求和
-------------------- -------------------- ----------- ----------
10 andy1 2000 2000
10 andy2 3000 5000
10 andy3 2000 7000
30 jack1 5000 12000
30 jack2 6000 18000
30 jack3 7000 25000
20 leno1 4000 29000
20 leno2 8000 37000
20 leno3 6000 43000


2 不連續求和
select deptno,ename,sal,sum(sal) over() 不連續求和 from emp;

DEPTNO ENAME SAL 不連續求和
-------------------- -------------------- ----------- ----------
10 andy1 2000 43000
10 andy2 3000 43000
10 andy3 2000 43000
20 leno1 4000 43000
20 leno2 8000 43000
20 leno3 6000 43000
30 jack1 5000 43000
30 jack2 6000 43000
30 jack3 7000 43000

3.
select deptno,ename,sal,
sum(sal) over(order by ename) 連續求和,
sum(sal) over() 總和,
100*round(sal/sum(sal) over(),4) "份額(%)"
from emp
/
DEPTNO ENAME SAL 連續求和 總和 份額(%)
-------------------- -------------------- ----------- ---------- ---------- ----------
10 andy1 2000 2000 43000 4.65
10 andy2 3000 5000 43000 6.98
10 andy3 2000 7000 43000 4.65
30 jack1 5000 12000 43000 11.63
30 jack2 6000 18000 43000 13.95
30 jack3 7000 25000 43000 16.28
20 leno1 4000 29000 43000 9.3
20 leno2 8000 37000 43000 18.6
20 leno3 6000 43000 43000 13.95


4.使用子分割槽查詢。
按部門薪水連續的總和.
(1)select deptno,sum(sal) over (partition by deptno order by ename) 按部門連續求總和 from emp;

DEPTNO 按部門連續求總和
-------------------- ----------------
10 2000
10 5000
10 7000
20 4000
20 12000
20 18000
30 5000
30 11000
30 18000

(2)按部門求總和
select deptno,sum(sal) over (partition by deptno) 按部門連續求總和 from emp ;

DEPTNO 按部門求總和
-------------------- ----------------
10 7000
10 7000
10 7000
20 18000
20 18000
20 18000
30 18000
30 18000
30 18000

(3)不按部門連續求總和
select deptno,sum(sal) over (order by deptno,ename) 不按部門連續求總和 from emp ;

DEPTNO 不按部門連續求總和
-------------------- ------------------
10 2000
10 5000
10 7000
20 11000
20 19000
20 25000
30 30000
30 36000
30 43000

(4)不按部門,求所有員工總和,效果等同於sum(sal)
select deptno,sum(sal) over (order by deptno,ename) 不按部門連續求總和 from emp ;

DEPTNO 不按部門連續求總和
-------------------- ------------------
10 2000
10 5000
10 7000
20 11000
20 19000
20 25000
30 30000
30 36000
30 43000

(5)select deptno,ename,sal,
sum(sal) over (partition by deptno order by ename) 部門連續求和,--各部門的薪水"連續"求和
sum(sal) over (partition by deptno) 部門總和, -- 部門統計的總和,同一部門總和不變
100*round(sal/sum(sal) over (partition by deptno),4) "部門份額(%)",
sum(sal) over (order by deptno,ename) 連續求和, --所有部門的薪水"連續"求和
sum(sal) over () 總和, -- 此處sum(sal) over () 等同於sum(sal),所有員工的薪水總和
100*round(sal/sum(sal) over (),4) "總份額(%)"
from emp;

DEPTNO ENAME SAL 部門連續求和 部門總和 部門份額(%) 連續求和 總和 總份額(%)
-------------------- -------------------- ----------- ------------ ---------- ----------- ---------- ---------- ----------
10 andy1 2000 2000 7000 28.57 2000 43000 4.65
10 andy2 3000 5000 7000 42.86 5000 43000 6.98
10 andy3 2000 7000 7000 28.57 7000 43000 4.65
20 leno1 4000 4000 18000 22.22 11000 43000 9.3
20 leno2 8000 12000 18000 44.44 19000 43000 18.6
20 leno3 6000 18000 18000 33.33 25000 43000 13.95
30 jack1 5000 5000 18000 27.78 30000 43000 11.63
30 jack2 6000 11000 18000 33.33 36000 43000 13.95
30 jack3 7000 18000 18000 38.89 43000 43000 16.28

(6)TOP-N查詢
6.1查詢各部門中工資最高的記錄
select * from(select deptno,ename,sal,row_number() over(partition by deptno order by sal desc) topn from emp) where topn=1;

DEPTNO ENAME SAL TOPN
-------------------- -------------------- ----------- ----------
10 andy2 3000 1
20 leno2 8000 1
30 jack3 7000 1

6.2按薪水高低對每個員工在本部門和整個公司內的排名進行排序。
select deptno,ename,sal,dense_rank() over(partition by deptno order by sal desc nulls last) as dept_ranking,
dense_rank() over(order by sal desc nulls last) as company_ranking
from emp;

DEPTNO ENAME SAL DEPT_RANKING COMPANY_RANKING
-------------------- -------------------- ----------- ------------ ---------------
20 leno2 8000 1 1
30 jack3 7000 1 2
20 leno3 6000 2 3
30 jack2 6000 2 3
30 jack1 5000 3 4
20 leno1 4000 3 5
10 andy2 3000 1 6
10 andy1 2000 2 7
10 andy3 2000 2 7

5 視窗windows
視窗子句是資料的滑動視窗,該視窗的分析函式將視窗看成一組。
select deptno "部門ID",ename "部門名稱",sal "工資",
sum(sal) over(partition by deptno order by ename rows 2 preceding) "sliding total"
from emp order by deptno,ename;

partition by deptno:相當於group by deptno
rows 2:表示前兩行相加
preceding:表示從每個部門的第一行開始。


6 範圍視窗
Range windows僅對資料值和日期型別資料有效。(sal)
select deptno,ename,sal, count(*) over(order by sal asc range 3 preceding) 總計

7 行視窗
是物理單元,包含在視窗中的物理行數。對資料型別沒有限制。
計算每個記錄與其之前的2個記錄的平均工資。
set numformat 9999
select ename,sal,
avg(sal) over(order by deptno asc rows 2 preceding) avgasc,
count(*) over(order by deptno asc rows 2 preceding) cntasc,
avg(sal) over(order by deptno desc rows 2 preceding) avgdes,
count(*) over(order by deptno desc rows 2 preceding) cntdes
from emp order by deptno;

ENAME SAL AVGASC CNTASC AVGDES CNTDES
-------------------- ----------- ---------- ---------- ---------- ----------
andy1 2000 2000 1 3666.66666 3
andy2 3000 2500 2 5666.66666 3
andy3 2000 2333.33333 3 2333.33333 3
leno1 4000 3000 3 5333.33333 3
leno2 8000 4666.66666 3 6333.33333 3
leno3 6000 6000 3 6000 3
jack1 5000 6333.33333 3 5500 2
jack2 6000 5666.66666 3 6000 1
jack3 7000 6000 3 6000 3

8 確定每組中的第一行或最後一行
使用first_vale和last_value函式可從一組中選擇每一行和最後一行
統計工資每個部門最低或最高的員工資訊。
select deptno,ename,sal,first_value(ename) over(partition by deptno order by sal asc) as min_sal_has
from emp
order by deptno,ename;

select deptno,ename,sal,first_value(ename) over(partition by deptno order by sal desc) as min_sal_has
from emp
order by deptno,ename;

9 統計各班成績第一名的同學資訊 NAME CLASS S
----- ----- ----------------------
fda 1 80
ffd 1 78
dss 1 95
cfe 2 74
gds 2 92
gf 3 99
ddd 3 99
adf 3 45
asdf 3 55
3dd 3 78

通過:
--
select * from
(
select name,class,s,rank()over(partition by class order by s desc) mm from t2
)
where mm=1
--
得到結果:
NAME CLASS S MM
----- ----- ---------------------- ----------------------
dss 1 95 1
gds 2 92 1
gf 3 99 1
ddd 3 99 1

注意:
1.在求第一名成績的時候,不能用row_number(),因為如果同班有兩個並列第一,row_number()只返回一個結果
2.rank()和dense_rank()的區別是:
--rank()是跳躍排序,有兩個第二名時接下來就是第四名
--dense_rank()l是連續排序,有兩個第二名時仍然跟著第三名

二:開窗函式
開窗函式指定了分析函式工作的資料視窗大小,這個資料視窗大小可能會隨著行的變化而變化,舉例如下:
1:
over(order by salary) 按照salary排序進行累計,order by是個預設的開窗函式
over(partition by deptno)按照部門分割槽
2:
over(order by salary range between 5 preceding and 5 following)
每行對應的資料視窗是之前行幅度值不超過5,之後行幅度值不超過5
例如:對於以下列
aa
1
2
2
2
3
4
5
6
7
9

sum(aa)over(order by aa range between 2 preceding and 2 following)
得出的結果是
AA SUM
---------------------- -------------------------------------------------------
1 10
2 14
2 14
2 14
3 18
4 18
5 22
6 18
7 22
9 9

就是說,對於aa=5的一行 ,sum為 5-1<=aa<=5+2 的和
對於aa=2來說 ,sum=1+2+2+2+3+4=14 ;
又如 對於aa=9 ,9-1<=aa<=9+2 只有9一個數,所以sum=9 ;

3:其它:
over(order by salary rows between 2 preceding and 4 following)
每行對應的資料視窗是之前2行,之後4行
4:下面三條語句等效:
over(order by salary rows between unbounded preceding and unbounded following)
每行對應的資料視窗是從第一行到最後一行,等效:
over(order by salary range between unbounded preceding and unbounded following)
等效
over(partition by null)