Oracle实战优化:递归+分析函数+OLAP函数的应用

原创文章,转载请注明出处,谢谢合作。
https://blog.csdn.net/DarianMograine/article/details/108561662

日前,笔者在工作中被人问到这样一个问题:

Oracle数据库中,如何优化取每一行数据在当前表中同分组数据的汇总。

当前表取出数据量级1W+,原逻辑是先取每行后对表进行N次访问每次读取数据,虽然是主键回表汇总金额,由于数据条数过多,页面返回时间无法预期,希望能够通过SQL层面优化。
如下图所示,希望得到每日当月累计营业额:

示例在这里插入图片描述
以上是问题的背景。

实际上,类似场景有很多,如:

  1. 汇总部分门店每日当月累计营业额
  2. 按规则自动分组匹配(银行对账、核销、单据对比等)

这里给大家推荐一个思路,利用递归+分组开窗函数+OLAP分析函数一个SQL实现数据的抓取。

我们先来看下实现的代码:

  1. 创建演示表
CREATE TABLE test_cb
(key    NUMBER
,iodate DATE
,amount NUMBER);
  1. 初始化模拟数据
INSERT INTO test_cb
  (key, iodate, amount)
  SELECT 1    key
        ,DATE '2020-05-01' iodate
        ,100  amount
  FROM   dual
  UNION ALL
  SELECT 1    key
        ,DATE '2020-05-01' iodate
        ,150  amount
  FROM   dual
  UNION ALL
  SELECT 1    key
        ,DATE '2020-05-03' iodate
        ,120  amount
  FROM   dual
  UNION ALL
  SELECT 2    key
        ,DATE '2020-05-01' iodate
        ,80   amount
  FROM   dual
  UNION ALL
  SELECT 2    key
        ,DATE '2020-05-02' iodate
        ,230  amount
  FROM   dual
  UNION ALL
  SELECT 2    key
        ,DATE '2020-05-04' iodate
        ,710  amount
  FROM   dual;
  1. 抓取数据的SQL代码
SELECT sub2.key
      ,sub2.iodate
      ,sub2.amount "当前行金额"
      ,dbms_aw.eval_number(substr(sys_connect_by_path(sub2.amount, '+'), 2)) "汇总到当前行金额"
      ,lag(dbms_aw.eval_number(substr(sys_connect_by_path(sub2.amount, '+')
                                     ,2))) over(PARTITION BY sub2.key ORDER BY sub2.iodate) "汇总到上一行金额"
FROM   (SELECT sub1.key
              ,sub1.iodate
              ,SUM(sub1.amount) amount
              ,row_number() over(PARTITION BY sub1.key ORDER BY sub1.iodate) rn
        FROM   test_cb sub1
        GROUP  BY sub1.iodate
                 ,sub1.key) sub2
CONNECT BY nocycle(PRIOR sub2.rn = sub2.rn - 1
            AND    PRIOR sub2.key = sub2.key)
START  WITH rn = 1

接下来我们来看一下这段代码是怎么解决的这个问题:

  1. 我们利用分析函数row_number对数据以字段key为分组依据做分组牌序,得到每行数据展示的顺序

  2. 利用递归天然有序的特性,将之前行的金额形成形如“+200+100+150”这样的字符串并去掉第一个+,这样我们就得到一个字符串200+100+150,亦即代码中**substr(sys_connect_by_path(sub2.amount, ‘+’), 2)**部分。

  3. 利用OLAP函数dbms_aw.eval_number计算200+100+150的值,类似excel里=200+100+150的功能得到450。

  4. 利用分析函数lag在以key为基准,以iodate为排序字段的分组中获取上一行的步骤3金额,即得到上一行的汇总金额。

优化后的SQL中由于使用的是Oracle的内置函数,速度基本可以控制在2s以内。

是不是很神奇?感兴趣的筒子可以一试。

原创文章,转载请注明出处,谢谢合作。
https://blog.csdn.net/DarianMograine/article/details/108561662

猜你喜欢

转载自blog.csdn.net/DarianMograine/article/details/108561662
今日推荐