Oracle SQL 查詢優化.Part6

一、聚集函數

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;

查詢的結果集與上邊是一樣的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章