一、聚集函數
1. null 對聚集函數的影響:
聚集函數會忽略 null,不做統計。這個對於 sum 來說沒什麼影響,但是對於 avg、count 來說,就會出現意料之外的結果。所以,通常來說,按照需求來決定是否要把空值轉爲 0。
-- select select * from emp;
-- 沒用 nvl select count(emp.empsalary) emp_cou, avg(emp.empsalary) emp_avg, sum(emp.empsalary) emp_sum from emp;
-- 用 nvl select count(nvl(emp.empsalary, 0)) emp_cou, avg(nvl(emp.empsalary, 0)) emp_avg, sum(nvl(emp.empsalary, 0)) emp_sum from emp;
從上邊對比可以看出,聚集函數會忽略掉對 null 的統計。
二、分析函數
1. 分析函數之開窗 —— over && range、rows
range、rows 是兩個不同的窗口:
- range 是邏輯窗口,是指定當前行對應值的取值範圍,列數不固定,對應列都包含在內。
- rows 是物理窗口,是根據 order by 排序後,取前 N 行,後 M 行的數據計算(與當前值無關,只與排序後的行號相關)。
select emp.empno, emp.empname, emp.empsalary, sum(emp.empsalary) over(order by emp.empsalary) normal_sum, sum(emp.empsalary) over(order by emp.empsalary range between unbounded preceding and current row) range_unbou_sum, sum(emp.empsalary) over(order by emp.empsalary rows between unbounded preceding and current row) rows_unbou_sum, sum(emp.empsalary) over(order by emp.empsalary range between 1 preceding and 1 following) range_sum, sum(emp.empsalary) over(order by emp.empsalary rows between 1 preceding and 1 following) rows_sum from emp;
上邊的例子中,range 是取所有對應值的列,而 rows 是取排序後對應物理行號的列:range_unbou_sum 和 rows_unbou_sum 對比,range 是取從第一行到當前行對應值對應的所有行;而 rows 是從第一行到當前行。range_sum 和 rows_sum 對比,range_sum 中 ‘1’ preceding 的 ‘1’ 其實已經沒有了意義,就是找當前行的這列值對應的所有行;而 rows_sum 是找物理當前行的前一行和當前行的下一行。(ps:這裏講起來邏輯好複雜。總而一句話,range 是和當前行的這列值相等的所有行;rows 是排序後的物理行,和值沒關係。)
這裏還需要說下 unbounded、current、preceding、following...
unbounded preceding 第一行 current row 當前行 N preceding 向前 N 行 M following 向後 M 行
2. 分析函數之序列號 —— partition by && row_number、rank、dense_rank
三個分析函數 row_number()、rank()、dense_rank() 都是生成序號的分析函數,通過 partition by 分組之後,按照 order by 對應的列對每組的每條記錄生成一個排序後的序號。至於三個分析函數之間的區別,看一個例子就一目瞭然啦:
-- partition by select deptno, empno, empsalary, row_number() over(partition by deptno order by empsalary) rm_sal, dense_rank() over(partition by deptno order by empsalary) dense_sal, rank() over(partition by deptno order by empsalary) rank_sal from emp;
row_number() 不管 order by 的列值是多少,按照排序後的結果依次遞增產生序列號。dense_rank() 和 rank() 函數對於 order by 相同列值的列,都會產生相同的序號,比如 emp01 和 emp03 兩列的序號都是 2。但是它們的區別是:dense_rank() 產生的序列號是連續的,rank() 產生的序列號是不連續的。舉一個 rank() 函數的應用場景:前兩個學生都考100,並列第一名,第三個學生考 99,那麼他只能是第三名。
3. 分析函數之最值所在行 —— keep && first、last
-- first、last 返回最小值所在行和最大值所在行 select emp.empno, emp.empname, emp.empsalary, to_char(wmsys.wm_concat(empname) keep(dense_rank first order by emp.empsalary) over(partition by emp.deptno)) min_sal_emp, to_char(wmsys.wm_concat(empname) keep(dense_rank last order by emp.empsalary) over(partition by emp.deptno)) max_sal_emp from emp where emp.deptno = 'dept02' order by emp.empsalary;
上邊 SQL 執行結果顯示,當最值有重複的時候(最小值 2000 對應的“Clare 和 Adela”、最大值 9000 對應的“Mark.李 和 大Boss”),keep 返回的是一個數據集。
4. 分析函數之比例的計算 —— over && ratio_to_report
- 利用 sum()、over() 實現
-- 利用 sum()、over() 實現 select emp.empno, emp.empname, emp.empsalary, sum(emp.empsalary) over() sal_total, round(emp.empsalary / (sum(emp.empsalary) over()), 4) * 100 || '%' sal_percent from emp
- 利用 ratio_to_report 實現
查詢的結果集與上邊是一樣的。-- 利用 ratio_to_report 實現 select emp.empno, emp.empname, emp.empsalary, sum(emp.empsalary) over() sal_total, round(ratio_to_report(emp.empsalary) over(), 4) * 100 || '%' sal_percent from emp;