ORACLE - 開窗+分析函數

      分析函數是Oracle專門用於解決複雜報表統計需求的功能強大的函數,它可以在數據中進行分組然後計算基於組的某種統計值,並且每一組的每一行都可以返回一個統計值。

     開窗函數指定了函數所能影響的窗口範圍,也就是說在這個窗口範圍中都可以受到函數的影響,有些分析函數就是開窗函數。

 

開窗函數over 存在兩種情況 一種只使用 partition by 做分組 後 全組範圍內分析,第二種是 加 order by 是一個分組後連續分析的概念(對 連續結果集 做操作)

 

 

原數據列如下

 

1、sum/avg... + over(partition by )  分組全範圍計算

with temp as (
    select 1 id, 'A' name, 20 age, '研發部' dept, 1000 amt, 1 state from dual union all
    select 2 id, 'B' name, 22 age, '測試部' dept, 2000 amt, 0 state from dual union all
    select 3 id, 'C' name, 24 age, '架構部' dept, 3000 amt, 1 state from dual union all
    select 4 id, 'D' name, 23 age, '研發部' dept, 4000 amt, 1 state from dual union all
    select 5 id, 'E' name, 23 age, '測試部' dept, 5000 amt, 0 state from dual union all
    select 6 id, 'F' name, 22 age, '架構部' dept, 6000 amt, 1 state from dual union all
    select 7 id, 'G' name, 25 age, '研發部' dept, 7000 amt, 1 state from dual union all
    select 8 id, 'H' name, 25 age, '測試部' dept, 8000 amt, 0 state from dual
)
select
  temp.*,
  sum(amt) over(partition by age) age_amt_sum, -- 每個年齡段的 工資總和
  avg(amt) over(partition by age) age_amt_avg,  -- 每個年齡段的 平均工資
  count(*) over(partition by age) age_nums, -- 每個年齡段 員工數
  count(distinct name) over(partition by age) age_disname_nums, -- 每個年齡段 員工數 去重
  count(distinct case when state = 1 then name end) over() state1_nums, -- state = 1 的數量
  count(distinct case when state = 0 then name end) over() state0_nums, -- state = 0 的數量
  max(amt) over(partition by age) age_max_amt, -- 每個年齡段的最大工資
  min(amt) over(partition by age) age_min_amt, -- 每個年齡段的最小工資
  row_number() over(partition by age order by amt desc) rn, -- 每個年齡段工資排名
  RATIO_TO_REPORT(amt) over(partition by age) rate, -- 每個年齡段內工資佔比
  sum(amt) over(order by age rows between unbounded preceding and current row) amt_sum, -- 從第一行到當前行 amt 累計求和
  sum(amt) over(order by age range between unbounded preceding and current row) amt_sum2 -- 從第一行到當前行 age_amt_sum 累計求和(同樣的年齡是一個範圍)  
from temp;

結果集

其中排名函數有3個

row_number( ):返回一個唯一的值,當碰到相同數據時,排名按照記錄集中記錄的順序依次遞增。

rank( ):返回一個唯一的值,當碰到相同的數據時,此時所有相同數據的排名是一樣的,同時會在最後一條相同記錄和下一條不同記錄的排名之間空出排名。

 dense_rank( ):返回一個唯一的值,當碰到相同數據時,此時所有相同數據的排名都是一樣的,同時會在最後一條相同記錄和下一條不同記錄的排名之間緊鄰遞增。
over關鍵字後的括號中的選項爲空,則開窗函數會對結果集中的所有行進行聚合運算;over關鍵字後的括號中的選項爲不爲空,則按照括號中的範圍進行聚合運算。

 

 

2、sum/avg  ... + over(partition by order by)  分組且連續計算

with temp as (
    select 1 id, 'A' name, 20 age, '研發部' dept, 1000 amt from dual union all
    select 2 id, 'B' name, 22 age, '測試部' dept, 2000 amt from dual union all
    select 3 id, 'C' name, 24 age, '架構部' dept, 3000 amt from dual union all
    select 4 id, 'D' name, 23 age, '研發部' dept, 4000 amt from dual union all
    select 5 id, 'E' name, 23 age, '測試部' dept, 5000 amt from dual union all
    select 6 id, 'F' name, 22 age, '架構部' dept, 6000 amt from dual union all
    select 7 id, 'G' name, 25 age, '研發部' dept, 7000 amt from dual union all
    select 8 id, 'H' name, 25 age, '測試部' dept, 8000 amt from dual
)
select
  temp.*,
  sum(amt) over(partition by dept order by age) dept_amt_sum, -- 每個部門 按照年齡排序 連續累計和
  avg(amt) over(partition by dept order by age) dept_amt_avg,  -- 每個部門 按照年齡段的 連續求平均工資
  count(*) over(partition by dept order by age) dept_age_nums,  -- 每個部門根據年齡 報數 (連續計數)
  max(amt) over(partition by dept order by age) age_max_amt     -- 每個部門根據年齡排序 連續取最大值 
from temp;

結果集

 

 

 

3、小例子: 利用 log + over 計算同比環比值

with temp as (
    select 1 id, 2017 t_year, 6 t_month, '6000' t_amt, 'A' dept from dual union all
    select 2 id, 2017 t_year, 7 t_month, '7000' t_amt, 'A' dept from dual union all
    select 3 id, 2017 t_year, 8 t_month, '8000' t_amt, 'A' dept from dual union all
    select 4 id, 2017 t_year, 9 t_month, '9000' t_amt, 'A' dept from dual union all
    select 5 id, 2017 t_year, 10 t_month, '10000' t_amt, 'A' dept from dual union all
    select 6 id, 2017 t_year, 11 t_month, '11000' t_amt, 'A' dept from dual union all
    select 7 id, 2017 t_year, 12 t_month, '12000' t_amt, 'A' dept from dual union all
    select 8 id, 2018 t_year, 1 t_month, '1000' t_amt, 'A' dept from dual union all
    select 9 id, 2018 t_year, 2 t_month, '2000' t_amt, 'A' dept from dual union all
    select 10 id, 2018 t_year, 3 t_month, '3000' t_amt, 'A' dept from dual union all
    select 11 id, 2018 t_year, 4 t_month, '4000' t_amt, 'A' dept from dual union all
    select 12 id, 2018 t_year, 5 t_month, '5000' t_amt, 'A' dept from dual union all
    select 13 id, 2018 t_year, 6 t_month, '6000' t_amt, 'A' dept from dual union all
    select 14 id, 2018 t_year, 7 t_month, '7000' t_amt, 'A' dept from dual union all
    select 15 id, 2018 t_year, 8 t_month, '8000' t_amt, 'A' dept from dual union all
    select 16 id, 2018 t_year, 9 t_month, '9000' t_amt, 'A' dept from dual union all
    select 17 id, 2018 t_year, 10 t_month, '10000' t_amt, 'A' dept from dual union all
    select 18 id, 2018 t_year, 11 t_month, '11000' t_amt, 'A' dept from dual union all
    select 19 id, 2018 t_year, 12 t_month, '12000' t_amt, 'A' dept from dual union all
    select 20 id, 2019 t_year, 1 t_month, '1000' t_amt, 'A' dept from dual union all
    select 21 id, 2019 t_year, 2 t_month, '2000' t_amt, 'A' dept from dual union all
    select 22 id, 2019 t_year, 3 t_month, '3000' t_amt, 'A' dept from dual union all
    select 23 id, 2019 t_year, 4 t_month, '4000' t_amt, 'A' dept from dual union all
    select 24 id, 2019 t_year, 5 t_month, '5000' t_amt, 'A' dept from dual union all
    select 25 id, 2019 t_year, 6 t_month, '6000' t_amt, 'A' dept from dual union all
    select 26 id, 2019 t_year, 7 t_month, '7000' t_amt, 'A' dept from dual union all
    
    select 28 id, 2017 t_year, 6 t_month, '6000' t_amt, 'B' dept from dual union all
    select 29 id, 2017 t_year, 7 t_month, '7000' t_amt, 'B' dept from dual union all
    select 30 id, 2017 t_year, 8 t_month, '8000' t_amt, 'B' dept from dual union all
    select 31 id, 2017 t_year, 9 t_month, '9000' t_amt, 'B' dept from dual union all
    select 32 id, 2017 t_year, 10 t_month, '10000' t_amt, 'B' dept from dual union all
    select 33 id, 2017 t_year, 11 t_month, '11000' t_amt, 'B' dept from dual union all
    select 34 id, 2017 t_year, 12 t_month, '12000' t_amt, 'B' dept from dual union all
    select 35 id, 2018 t_year, 1 t_month, '1000' t_amt, 'B' dept from dual union all
    select 36 id, 2018 t_year, 2 t_month, '2000' t_amt, 'B' dept from dual union all
    select 37 id, 2018 t_year, 3 t_month, '3000' t_amt, 'B' dept from dual union all
    select 38 id, 2018 t_year, 4 t_month, '4000' t_amt, 'B' dept from dual union all
    select 39 id, 2018 t_year, 5 t_month, '5000' t_amt, 'B' dept from dual union all
    select 40 id, 2018 t_year, 6 t_month, '6000' t_amt, 'B' dept from dual union all
    select 41 id, 2018 t_year, 7 t_month, '7000' t_amt, 'B' dept from dual union all
    select 42 id, 2018 t_year, 8 t_month, '8000' t_amt, 'B' dept from dual union all
    select 43 id, 2018 t_year, 9 t_month, '9000' t_amt, 'B' dept from dual union all
    select 44 id, 2018 t_year, 10 t_month, '10000' t_amt, 'B' dept from dual union all
    select 45 id, 2018 t_year, 11 t_month, '11000' t_amt, 'B' dept from dual union all
    select 46 id, 2018 t_year, 12 t_month, '12000' t_amt, 'B' dept from dual union all
    select 47 id, 2019 t_year, 1 t_month, '1000' t_amt, 'B' dept from dual union all
    select 48 id, 2019 t_year, 2 t_month, '2000' t_amt, 'B' dept from dual union all
    select 49 id, 2019 t_year, 3 t_month, '3000' t_amt, 'B' dept from dual union all
    select 50 id, 2019 t_year, 4 t_month, '4000' t_amt, 'B' dept from dual union all
    select 51 id, 2019 t_year, 5 t_month, '5000' t_amt, 'B' dept from dual union all
    select 52 id, 2019 t_year, 6 t_month, '6000' t_amt, 'B' dept from dual union all
    select 53 id, 2019 t_year, 7 t_month, '7000' t_amt, 'B' dept from dual 
)
select
    tab.dept,
    tab.t_year,
    tab.t_month,
    tab.t_amt,
    case -- 判斷是否爲上一個月
      when (tab.pre_month+1 = tab.t_month) or (tab.t_month = 1 and tab.pre_month=12 and tab.pre_year+1 = tab.t_year) then
        decode(tab.pre_amt,null,null,0,null,round(tab.t_amt/tab.pre_amt,2)) 
      else
        null
    end as 環比, 
    case -- 判斷是否上一年 同一個月
      when (tab.t_month = tab.last_month and tab.last_year+1 = tab.t_year) then
        decode(tab.last_amt,null,null,0,null,round(tab.t_amt/tab.last_amt,2)) 
      else
        null
    end as 同比  
        
from (
select
   t.dept,
   t.t_year,
   t.t_month,
   t.t_amt,
   lag(t.t_year,1,-1) over(partition by t.dept order by t.t_year,t.t_month) pre_year, -- 上個月所在年
   lag(t.t_month,1,-1) over(partition by t.dept order by t.t_year,t.t_month) pre_month, -- 上個月
   lag(t.t_amt,1,null) over(partition by t.dept order by t.t_year,t.t_month) pre_amt, -- 上個月值
   lag(t.t_year,12,-1) over(partition by t.dept order by t.t_year,t.t_month) last_year, -- 去年
   lag(t.t_month,12,-1) over(partition by t.dept order by t.t_year,t.t_month) last_month, -- 去年同月
   lag(t.t_amt,12,null) over(partition by t.dept order by t.t_year,t.t_month) last_amt  -- 去年同月值
from temp t
) tab

 

 

 

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