oracle计算占比和环比增长率案例

前言

        在数据库做统计分析的时候,我们可能需要结合不同的需求计算数据的汇总、平均、占比、环比、同步等等,那么在通过sql硬编码前我们是否应该考虑数据的各种问题,以及在编写代码时,我们是否应该通过各种函数进行数据预处理,避免最终预览数据时出现各种错误呢?

需求:

        根据科室、时间维度分组, 对不同科室人数、科室总金额进行统计汇总,以及计算科室不同药品的使用情况 如:各药品的占比、环比增长率等等?

1)对科室、时间维度进行分组,对人数进行count,对总金额进行sum
注:使用group by +聚合函数即可(sumcount、…)等
2)对药品的使用情况 进行占比分析、环比增长率
注:药品占比分析 某类型药费用总额/费用总额 * 100
需考虑情况:某类型药费用总额 或 费用总额 为 0的情况

解读环比增长率:环比增长率=(本期数-上期数)÷上期数×100%
药品环比增长率 (本月费用总额-上月费用总额)上月费用总额*100

需考虑3种情况:

1) 本月费用总额、上月费用总额 同时为 0 如 (0-0)/0 则提示ORA-01476
2) 本月费用总额、上月费用总额 不为0但是数据相等 (1000-1000)/1000=0
3) 本月费用总额不为0、上月费用总额为0 (1000-0)/0 则提示ORA-01476
所以需要针对该3种情况进行处理,可通过case when 函数



案例基础数据准备

建表使用中文命名字段是为了方便大家阅读,实际工作情况下 不推荐此骚操作。

CREATE TABLE MY_TABLE_TEST (
  "结算时间" VARCHAR2(255),
  "科室代码" VARCHAR2(255),
  "科室名称" VARCHAR2(255),
  "人次" NUMBER,
  "次均费用" NUMBER,
  "费用明细" NUMBER,
  "中药" NUMBER,
  "中成药费" NUMBER,
  "西药" NUMBER,
  "耗材" NUMBER );
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2021-12', '101', 'AAA', 8, 269.65, 2157.19, 0, 0, 259.19, 0);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-01', '101', 'AAA', 72, 3081.32, 221854.81, 0, 80.85, 34520.06, 8400.8);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-02', '101', 'AAA', 58, 4621.79, 268063.98, 0, 181.44, 33361.24, 53853.5);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-03', '101', 'AAA', 84, 2970.27, 249502.28, 413.64, 421.76, 40433.68, 11174.9);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-04', '101', 'AAA', 65, 2762.42, 179557.49, 571.45, 432.8, 30034.14, 5624.8);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-05', '101', 'AAA', 31, 2362.88, 73249.27, 0, 205.2, 11180.97, 2207.7);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-01', '202', 'BBB', 91, 4636.3, 421903.67, 0, 2850.69, 51995.28, 15754.6);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-02', '202', 'BBB', 89, 5299.69, 471672, 0, 3606.04, 63211.36, 18940.5);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-03', '202', 'BBB', 130, 5277.78, 686111.03, 0, 5606.76, 86714.17, 25642.9);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-04', '202', 'BBB', 117, 4797.85, 561347.96, 0, 4996.95, 74157.71, 22723.1);
INSERT INTO MY_TABLE_TEST("结算时间", "科室代码", "科室名称", "人次", "次均费用", "费用明细", "中药", "中成药费", "西药", "耗材") VALUES ('2022-05', '202', 'BBB', 45, 5563.04, 250336.67, 0, 1773.8, 32920.17, 10349.9);


sql统计示例

(复制可使用,注释的地方主要为了减少可阅读性)

select 结算时间,科室代码,
       科室名称,
       count(科室名称) as 人次,round(sum(费用明细)/count(科室名称),2) as 次均费用,sum(费用明细) AS 费用明细,
       sum(中药) aS 中药,
       replace(to_char(round((sum(中药)/ sum(费用明细)*100.00),2),'fm999990.9999')||'%','0.%','0%')  AS 中药占比,  --将相除的数据转成字符串,并将可能出现字符进行替换
       replace(  replace( replace(to_char( (case
            when sum(中药) - LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = 0 then LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) --判断(0-0=0)本月-减上月=0 的情况。不判断可能会出现 ORA-01476: 除数为0
            when sum(中药) - LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = sum(中药) then 0   --判断 (5000-500=0)判断本月和上月数据一样相减等于0 的情况。不判断可能会出现 ORA-01476: 除数为0
            else round((sum(中药) - LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)) /
                       decode(LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间), 0, 1,
                              LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)),
                       4)     --判断(本月-上月 )/上月 不等于0 的情况
                     *100 end) , 'fm999990.9999') || '%', '0.%',   '0%') ,'-1.%','-100%'),'1.%','100%')   as 中药环比增长,  --将最后数据转字符串,拼接 % ,并将可能出现字符进行替换
      LAG(sum(中药), 12, 0) OVER (PARTITION BY 科室名称 ORDER BY to_date(结算时间,'yyyy-mm')) as 环比-- 同比分析,与上个年度相同月份进行比较
     /*   sum(中成药费) as 中成药费,

       replace(to_char(round((sum(中成药费)/ sum(费用明细)*100.00),2),'fm999990.9999')||'%','0.%','0%') AS  中成药占比,
       replace( replace(replace(to_char((case
            when sum(中成药费) - LAG(sum(中成药费), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = 0 then LAG(sum(中成药费), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)
            when sum(中成药费) - LAG(sum(中成药费), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = sum(中成药费) then 0
            else round((sum(中成药费) - LAG(sum(中成药费), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)) /
                       decode(LAG(sum(中成药费), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间), 0, 1,
                              LAG(sum(中成药费), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)),
                       4)*100 end) , 'fm999990.9999') || '%', '0.%',   '0%'),'-1.%','-100%'),'1.%','100%')    as 中成药费环比增长,
      sum(西药) as 西药 ,
      replace(to_char(round((sum(西药)/sum(费用明细)*100.00),2),'fm999990.9999')||'%','0.%','0%') AS 西药占比,
      replace(  replace( replace(to_char((case
            when sum(西药) - LAG(sum(西药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = 0 then LAG(sum(西药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)
            when sum(西药) - LAG(sum(西药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = sum(西药) then 0
            else round((sum(西药) - LAG(sum(西药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)) /
                       decode(LAG(sum(西药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间), 0, 1,
                              LAG(sum(西药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)),
                       4)*100 end) , 'fm999990.9999') || '%', '0.%',   '0%'),'-1.%','-100%'),'1.%','100%')     as 西药环比增长,
       sum(耗材) as  耗材,
      replace(to_char(round((sum(耗材)/sum(费用明细)*100.00),2),'fm999990.9999')||'%','0.%','0%')  AS 耗材占比 ,
      replace(  replace( replace(to_char((case
            when sum(耗材) - LAG(sum(耗材), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = 0 then  LAG(sum(耗材), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)
            when sum(耗材) - LAG(sum(耗材), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = sum(耗材) then 0
            else round((sum(耗材) - LAG(sum(耗材), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)) /
                       decode(LAG(sum(耗材), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间), 0, 1,
                              LAG(sum(耗材), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)),
                       4)*100 end) , 'fm999990.9999') || '%', '0.%',   '0%') ,'-1.%','-100%')  ,'1.%','100%')       as 耗材环比增长*/
from  MY_TABLE_TEST  where  科室名称 in ('AAA','BBB')
  group by 科室代码 ,  科室名称,结算时间
order  by 科室名称  desc;

sql示例分析环比增长率

replace(  replace( replace(to_char( (case
        when sum(中药) - LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = 0 then LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) --判断(0-0=0)本月-减上月=0 的情况。不判断可能会出现 ORA-01476: 除数为0
        when sum(中药) - LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间) = sum(中药) then 0   --判断 (5000-500=0)判断本月和上月数据一样相减等于0 的情况。不判断可能会出现 ORA-01476: 除数为0
        else round((sum(中药) - LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)) /
                   decode(LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间), 0, 1,
                          LAG(sum(中药), 1, 0) OVER (PARTITION BY 科室名称 ORDER BY 结算时间)),
                   4)     --判断(本月-上月 )/上月 不等于0 的情况
                 *100 end) , 'fm999990.9999') || '%', '0.%',   '0%') ,'-1.%','-100%'),'1.%','100%')   as 中药环比增长

在这里插入图片描述
1、通过case when 判断数据的三种情况 ,防止出现整除为0 。
2、通过to_char(xxx,'fm999990.9999') 格式化数据转为字符串
3、通过||拼接符 拼接 %
4、将可能出现的数据 进行替换。

结尾

通过步骤拆解,所需要的功能基本实现,虽然该实现可能不是最优的,但希望能给部分所需要的同学带来一定的参考,所以当大家碰到此类问题基本不要慌,一步步去分析,一步步去尝试,最后,希望大家的技术越学越强。


                                                                                                 – 知识的价值不在占有,而在于分享和使用

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

项目实战哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值