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
发布了73 篇原创文章 · 获赞 78 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/SHIYUN123zw/article/details/96651056