oracle的分析函數over(Partition by...)
Sql代碼
over(Partition by...) 一個超級牛皮的ORACLE特有函數。
最近工作中才接觸到這個功能強大而靈活的函數。
oracle的分析函數over 及開窗函數
一:分析函數over
Oracle從8.1.6開始提供分析函數,分析函數用於計算基於組的某種聚合值,它和聚合函數的不同之處是
對於每個組返回多行,而聚合函數對於每個組只返回一行。
下面通過幾個例子來說明其應用。
1 1:統計某商店的營業額。 2 date sale 3 1 20 4 2 15 5 3 14 6 4 18 7 5 30 8 規則:按天統計:每天都統計前面幾天的總額 9 得到的結果: 10 DATE SALE SUM 11 ----- -------- ------ 12 1 20 20 --1天 13 2 15 35 --1天+2天 14 3 14 49 --1天+2天+3天 15 4 18 67 . 16 5 30 97 . 17 18 2:統計各班成績第一名的同學信息 19 NAME CLASS S 20 ----- ----- ---------------------- 21 fda 1 80 22 ffd 1 78 23 dss 1 95 24 cfe 2 74 25 gds 2 92 26 gf 3 99 27 ddd 3 99 28 adf 3 45 29 asdf 3 55 30 3dd 3 78 31 32 通過: 33 -- 34 select * from 35 ( 36 select name,class,s,rank()over(partition by class order by s desc) mm from t2 37 ) 38 where mm=1 39 -- 40 得到結果: 41 NAME CLASS S MM 42 ----- ----- ---------------------- ---------------------- 43 dss 1 95 1 44 gds 2 92 1 45 gf 3 99 1 46 ddd 3 99 1 47 48 注意: 49 1.在求第一名成績的時候,不能用row_number(),因為如果同班有兩個並列第一,row_number()只返回一個結果 50 2.rank()和dense_rank()的區別是: 51 --rank()是跳躍排序,有兩個第二名時接下來就是第四名 52 --dense_rank()l是連續排序,有兩個第二名時仍然跟着第三名 53 54 55 3.分類統計 (並顯示信息) 56 A B C 57 -- -- ---------------------- 58 m a 2 59 n a 3 60 m a 2 61 n b 2 62 n b 1 63 x b 3 64 x b 2 65 x b 4 66 h b 3 67 select a,c,sum(c)over(partition by a) from t2 68 得到結果: 69 A B C SUM(C)OVER(PARTITIONBYA) 70 -- -- ------- ------------------------ 71 h b 3 3 72 m a 2 4 73 m a 2 4 74 n a 3 6 75 n b 2 6 76 n b 1 6 77 x b 3 9 78 x b 2 9 79 x b 4 9 80 81 如果用sum,group by 則只能得到 82 A SUM(C) 83 -- ---------------------- 84 h 3 85 m 4 86 n 6 87 x 9 88 無法得到B列值 89 90 ===== 91 select * from test 92 93 數據: 94 A B C 95 1 1 1 96 1 2 2 97 1 3 3 98 2 2 5 99 3 4 6 100 101 102 ---將B欄位值相同的對應的C 欄位值加總 103 select a,b,c, SUM(C) OVER (PARTITION BY B) C_Sum 104 from test 105 106 A B C C_SUM 107 1 1 1 1 108 1 2 2 7 109 2 2 5 7 110 1 3 3 3 111 3 4 6 6 112 113 ---如果不需要已某個欄位的值分割,那就要用 null 114 115 eg: 就是將C的欄位值summary 放在每行后面 116 117 select a,b,c, SUM(C) OVER (PARTITION BY null) C_Sum 118 from test 119 120 A B C C_SUM 121 1 1 1 17 122 1 2 2 17 123 1 3 3 17 124 2 2 5 17 125 3 4 6 17 126 127 求個人工資占部門工資的百分比 128 129 SQL> select * from salary; 130 131 NAME DEPT SAL 132 ---------- ---- ----- 133 a 10 2000 134 b 10 3000 135 c 10 5000 136 d 20 4000 137 138 SQL> select name,dept,sal,sal*100/sum(sal) over(partition by dept) percent from salary; 139 140 NAME DEPT SAL PERCENT 141 ---------- ---- ----- ---------- 142 a 10 2000 20 143 b 10 3000 30 144 c 10 5000 50 145 d 20 4000 100 146 147 二:開窗函數 148 開窗函數指定了分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變化而變化,舉例如下: 149 1: 150 over(order by salary) 按照salary排序進行累計,order by是個默認的開窗函數 151 over(partition by deptno)按照部門分區 152 2: 153 over(order by salary range between 5 preceding and 5 following) 154 每行對應的數據窗口是之前行幅度值不超過5,之后行幅度值不超過5 155 例如:對於以下列 156 aa 157 1 158 2 159 2 160 2 161 3 162 4 163 5 164 6 165 7 166 9 167 168 sum(aa)over(order by aa range between 2 preceding and 2 following) 169 得出的結果是 170 AA SUM 171 ---------------------- ------------------------------------------------------- 172 1 10 173 2 14 174 2 14 175 2 14 176 3 18 177 4 18 178 5 22 179 6 18 180 7 22 181 9 9 182 183 就是說,對於aa=5的一行 ,sum為 5-1<=aa<=5+2 的和 184 對於aa=2來說 ,sum=1+2+2+2+3+4=14 ; 185 又如 對於aa=9 ,9-1<=aa<=9+2 只有9一個數,所以sum=9 ; 186 187 3:其它: 188 over(order by salary rows between 2 preceding and 4 following) 189 每行對應的數據窗口是之前2行,之后4行 190 4:下面三條語句等效: 191 over(order by salary rows between unbounded preceding and unbounded following) 192 每行對應的數據窗口是從第一行到最后一行,等效: 193 over(order by salary range between unbounded preceding and unbounded following) 194 等效 195 over(partition by null) 196 197 常用的分析函數如下所列: 198 199 row_number() over(partition by ... order by ...) 200 rank() over(partition by ... order by ...) 201 dense_rank() over(partition by ... order by ...) 202 count() over(partition by ... order by ...) 203 max() over(partition by ... order by ...) 204 min() over(partition by ... order by ...) 205 sum() over(partition by ... order by ...) 206 avg() over(partition by ... order by ...) 207 first_value() over(partition by ... order by ...) 208 last_value() over(partition by ... order by ...) 209 lag() over(partition by ... order by ...) 210 lead() over(partition by ... order by ...) 211 212 示例 213 SQL> select type,qty from test; 214 215 TYPE QTY 216 ---------- ---------- 217 1 6 218 2 9 219 220 SQL> select type,qty,to_char(row_number() over(partition by type order by qty))||'/'||to_char(count(*) over(partition by type)) as cnt2 from test; 221 222 TYPE QTY CNT2 223 ---------- ---------- ------------ 224 3 1/2 225 1 6 2/2 226 2 5 1/3 227 7 2/3 228 2 9 3/3 229 230 SQL> select * from test; 231 ---------- ------------------------------------------------- 232 1 11111 233 2 22222 234 3 33333 235 4 44444 236 237 SQL> select t.id,mc,to_char(b.rn)||'/'||t.id)e 238 2 from test t, 239 (select rownum rn from (select max(to_number(id)) mid from test) connect by rownum <=mid ))L 240 4 where b.rn<=to_number(t.id) 241 order by id 242 243 ID MC TO_CHAR(B.RN)||'/'||T.ID 244 --------- -------------------------------------------------- --------------------------------------------------- 245 1 11111 1/1 246 2 22222 1/2 247 2 22222 2/2 248 3 33333 1/3 249 3 33333 2/3 250 3 33333 3/3 251 44444 1/4 44444 2/4 252 4 44444 3/4CNOUG4 44444 4/4 253 254 10 rows selected 255 256 *******************************************************************
2,rank over 說明
排序:
---rank()over(order by 列名 排序)的結果是不連續的,如果有4個人,其中有3個是並列第1名,那么最后的排序結果結果如:1 1 1 4
select scoreid, studentid,COURSENAME,totalexamscore ,
rank()over(order by TOTALEXAMSCORE desc)orderbyNum
from SCORECOURSE a ,COURSESCORE b
where a.SCORECOURSEID = b.SCORECOURSEID
---dense_rank()over(order by 列名 排序)的結果是連續的,如果有4個人,其中有3個是並列第1名, 那么最后的排序結果如:1 1 1 2
select scoreid, studentid,COURSENAME,totalexamscore ,
dense_rank()over(order by TOTALEXAMSCORE desc)orderbyNum
from SCORECOURSE a ,COURSESCORE b
where a.SCORECOURSEID = b.SCORECOURSEID
----rank () OVER (PARTITION BY 列名 ORDER BY 列名 排序)使用分區方式獲取每門課程的最高分
SELECT *
FROM (SELECT scoreid, studentid, coursename, TOTALEXAMSCORE,
rank () OVER (PARTITION BY coursename ORDER BY TOTALEXAMSCORE DESC)orderbynum
FROM scorecourse a, coursescore b
WHERE a.scorecourseid = b.scorecourseid and studentID = 'xxxxx')
WHERE orderbynum < 2
-----使用over實現成績求和
-----SUM (totalexamscore) OVER (ORDER BY studentid) sum1 實現的是連續求和,如第一個學生的總評成績是30,則sum1就展示為30,到第二個學生成績出現的時候,則會依次累加
-----SUM (totalexamscore) OVER () sum2 就相當於是單純的求和,和直接使用sum是一致的
SELECT scoreid, studentid, totalexamscore,
SUM (totalexamscore) OVER (ORDER BY studentid) sum1,
SUM (totalexamscore) OVER () sum2
FROM coursescore
注:這個案例想了很久也沒想到很直觀的描述,還請大家親自去測試一下吧!!
語法:
rank() over (order by 排序字段 順序)
rank() over (partition by 分組字段 order by 排序字段 順序)
1.順序:asc|desc 名次與業務相關:
示例:求優秀學員,成績:降序 遲到次數:升序
2.分區字段:根據什么字段進行分區。
問題:分區與分組有什么區別?
•分區只是將原始數據進行名次排列(記錄數不變),
•分組是對原始數據進行聚合統計(記錄數變少,每組返回一條)。
注意:使用rank()over(order by 排序字段 順序)排序的時候,空值是最大的
(如果排序字段為null,可能造成在排序時將null字段排在最前面,影響排序的正確性。
所以建議將dense_rank()over(order by 列名 排序)改為dense_rank()over(order by 列名 排序 nulls last)
這樣只要排序字段為null,就會放在最后,而不會影響排序結果).