设为首页 收藏本站
查看: 1500|回复: 0

[经验分享] Oracle的 MODEL 查询

[复制链接]

尚未签到

发表于 2018-9-26 12:16:23 | 显示全部楼层 |阅读模式
  首先先造测试表/测试数据
  SQL> CREATE TABLE SALE_REPORT (
  2     SALE_DATE  DATE NOT NULL ,
  3     SALE_ITEM  VARCHAR(2) NOT NULL ,
  4     SALE_MONEY DECIMAL(10,2) NOT NULL
  5  )
  6  /
  Table created.
  SQL> DECLARE
  2    v_begin_day  DATE;
  3    v_end_day  DATE;
  4  BEGIN
  5    v_begin_day := TO_DATE('2009-01-01', 'YYYY-MM-DD');
  6    v_end_day := TO_DATE('2010-01-01', 'YYYY-MM-DD');
  7
  8    WHILE v_begin_day < v_end_day LOOP
  9      INSERT INTO SALE_REPORT VALUES(v_begin_day,  'A',  TO_NUMBER( TO_CHAR(v_begin_day, 'YYYY') ));
  10      INSERT INTO SALE_REPORT VALUES(v_begin_day,  'B',  TO_NUMBER( TO_CHAR(v_begin_day, 'MM') ));
  11      INSERT INTO SALE_REPORT VALUES(v_begin_day,  'C',  TO_NUMBER( TO_CHAR(v_begin_day, 'DD') ));
  12      v_begin_day := v_begin_day + 1;
  13    END LOOP;
  14  END;
  15  /
  PL/SQL procedure successfully completed.
  SQL> commit;
  Commit complete.
  原有的 SQL,查询每个月的销售额.
  With cte AS
  (
  SELECT
  TO_CHAR(SALE_DATE, 'MM') AS month,
  SUM(sale_money) AS sum_money
  FROM
  sale_report
  GROUP BY
  TO_CHAR(SALE_DATE, 'MM')
  )
  SELECT *
  FROM cte
  ORDER BY month;
  MO SUM_MONEY
  -- ----------
  01  62806
  02  56714
  03  62868
  04  60855
  05  62930
  06  60915
  07  62992
  08  63023
  09  61005
  10  63085
  11  61065
  MO SUM_MONEY
  -- ----------
  12  63147
  已选择12行。
  这里使用了 CTE, 如果对CTE不了解的话,也可以无视,就简单的认为本次的测试表只有12条记录就可以了。
  使用 MODEL 语句,实现类似 UNION ALL 操作的处理。
  -- 在原有SQL的 每个月的销售额 的基础上,进一步追加 每个季度 与 全年的销售额。
  With cte AS
  (
  SELECT
  TO_CHAR(SALE_DATE, 'MM') AS month,
  SUM(sale_money) AS sum_money
  FROM
  sale_report
  GROUP BY
  TO_CHAR(SALE_DATE, 'MM')
  )
  SELECT *
  FROM cte
  MODEL
  DIMENSION BY (month) -- 按照 月 为 维度
  MEASURES(sum_money) -- 数组的数据为 sum_money
  RULES(
  sum_money['Q1'] = sum_money['01']+sum_money['02']+sum_money['03'],
  sum_money['Q2'] = sum_money['04']+sum_money['05']+sum_money['06'],
  sum_money['Q3'] = sum_money['07']+sum_money['08']+sum_money['09'],
  sum_money['Q4'] = sum_money['10']+sum_money['11']+sum_money['12'],
  sum_money['YY'] = sum_money['Q1']+sum_money['Q2']+sum_money['Q3']+sum_money['Q4']
  )
  ORDER BY
  month;
  MO SUM_MONEY
  -- ----------
  01  62806
  02  56714
  03  62868
  04  60855
  05  62930
  06  60915
  07  62992
  08  63023
  09  61005
  10  63085
  11  61065
  MO SUM_MONEY
  -- ----------
  12  63147
  Q1    182388
  Q2    184700
  Q3    187020
  Q4    187297
  YY    741405
  已选择17行。
  先把上面的 MODEL 的部分说明一下:
  MODEL  关键字 必须
  DIMENSION BY 维度 必须
  MEASURES 指定作为数组的列 必须
  RULES 对数组进行各种操作的描述 可选
  -- 上一个SQL, 用 + 用的太多了。
  -- 这里用 SUM()[BETWEEN和AND] 返回特定范围内的数据单元
  -- 这里用 SUM()[ IN ] 返回特定范围内的数据单元
  With cte AS
  (
  SELECT
  TO_CHAR(SALE_DATE, 'MM') AS month,
  SUM(sale_money) AS sum_money
  FROM
  sale_report
  GROUP BY
  TO_CHAR(SALE_DATE, 'MM')
  )
  SELECT *
  FROM cte
  MODEL
  DIMENSION BY(month) -- 按照 月 为 维度
  MEASURES(sum_money) -- 数组的数据为 sum_money
  (
  sum_money['Q1'] = SUM(sum_money)[month BETWEEN '01' AND'03'],
  sum_money['Q2'] = SUM(sum_money)[month BETWEEN '04' AND'06'],
  sum_money['Q3'] = SUM(sum_money)[month BETWEEN '07' AND'09'],
  sum_money['Q4'] = SUM(sum_money)[month BETWEEN '10' AND'12'],
  sum_money['YY'] = SUM(sum_money)[month IN ('Q1', 'Q2', 'Q3', 'Q4')]
  )
  ORDER BY
  month;
  结果同上,就不重复复制粘贴了。
  增加一列数组的数据,通过 CURRENTV()  读取相邻数据的处理
  -- 本查询用于在 每一行上,增加一列(上月销售合计)
  -- 这里与前面不同点有以下几点:
  -- 1.CTE 当中,增加了一列 0 AS prev_sum_money
  -- 2.月的处理,由 TO_CHAR(SALE_DATE, 'MM') 变为 EXTRACT(MONTH FROM SALE_DATE)
  -- 3.在于 MEASURES 中有 2 列数据.也就是可以有2个数组,分别为 sum_money 与 prev_sum_money
  -- 4.使用了 FOR 语句,遍历数组从2月到12月
  -- 5.使用了 CURRENTV() 函数,取得当前数组索引,然后用来-1。从而获取 上月销售合计.
  With cte AS
  (
  SELECT
  EXTRACT(MONTH FROM SALE_DATE) AS month,
  SUM(sale_money) AS sum_money,
  0 AS prev_sum_money
  FROM
  sale_report
  GROUP BY
  EXTRACT(MONTH FROM SALE_DATE)
  )
  SELECT
  *
  FROM cte
  MODEL
  DIMENSION BY(month) -- 按照 月 为 维度
  MEASURES(sum_money, prev_sum_money) -- 数组的数据为 sum_money, prev_sum_money
  (
  prev_sum_money[FOR month FROM 2 TO 12 INCREMENT 1]
  = sum_money[CURRENTV() - 1]
  )
  ORDER BY
  month;
  MONTH SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- --------------
  1  62806      0
  2  56714     62806
  3  62868     56714
  4  60855     62868
  5  62930     60855
  6  60915     62930
  7  62992     60915
  8  63023     62992
  9  61005     63023
  10  63085     61005
  11  61065     63085
  MONTH SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- --------------
  12  63147     61065
  已选择12行。
  增加维度的处理
  -- 在前面的基础上,增加年的字段.
  -- 然后再维度中,增加 年的 维度.
  With cte AS
  (
  SELECT
  EXTRACT(YEAR FROM SALE_DATE) AS year,
  EXTRACT(MONTH FROM SALE_DATE) AS month,
  SUM(sale_money) AS sum_money,
  0 AS prev_sum_money
  FROM
  sale_report
  GROUP BY
  EXTRACT(YEAR FROM SALE_DATE),
  EXTRACT(MONTH FROM SALE_DATE)
  )
  SELECT
  *
  FROM cte
  MODEL
  DIMENSION BY (year, month)  -- 按照 年,月 为 维度
  MEASURES(sum_money, prev_sum_money) -- 数组的数据为 sum_money, prev_sum_money
  (
  prev_sum_money[2009, FOR month FROM 2 TO 12 INCREMENT 1]
  = sum_money[CURRENTV(), CURRENTV() - 1]
  )
  ORDER BY
  month;
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2009   1    62806     0
  2009   2    56714    62806
  2009   3    62868    56714
  2009   4    60855    62868
  2009   5    62930    60855
  2009   6    60915    62930
  2009   7    62992    60915
  2009   8    63023    62992
  2009   9    61005    63023
  2009     10    63085    61005
  2009     11    61065    63085
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2009     12    63147    61065
  已选择12行。
  IS PRESENT / PRESENTV() / PRESENTNNV()
  -- 在前面的基础上,增加2010年1月的 上月 数据 (数据库中无2010年数据)
  -- 这里的 IS PRESENT :当数据单元指定的记录在MODEL子句执行之前存在,则IS PRESENT返回TRUE。
  -- PRESENTV() 如果cell引用的记录在MODEL子句执行以前就存在,那么PRESENTV(cell,expr1,expr2)返回表达式expr1。如果这条记录不存在,则返回表达式expr2。
  -- PRESENTNNV() 如果cell引用的单元在MODEL子句执行之前已经存在,并且该单元的值不为空,则PRESENTNNV(cell,expr1,expr2)返回表达式expr1。如果记录不存在,或单元值为空值,则返回表达式expr2。
  With cte AS
  (
  SELECT
  EXTRACT(YEAR FROM SALE_DATE) AS year,
  EXTRACT(MONTH FROM SALE_DATE) AS month,
  SUM(sale_money) AS sum_money,
  0 AS prev_sum_money
  FROM
  sale_report
  GROUP BY
  EXTRACT(YEAR FROM SALE_DATE),
  EXTRACT(MONTH FROM SALE_DATE)
  )
  SELECT
  *
  FROM cte
  MODEL
  DIMENSION BY(year, month)  -- 按照 年,月 为 维度
  MEASURES(sum_money, prev_sum_money) -- 数组的数据为 sum_money, prev_sum_money
  (
  prev_sum_money[2009, FOR month FROM 2 TO 12 INCREMENT 1]
  = sum_money[CURRENTV(), CURRENTV() - 1],
  prev_sum_money[2010, 1] = sum_money[2009, 12],
  prev_sum_money[2010, FOR month FROM 2 TO 3 INCREMENT 1]
  = CASE WHEN sum_money[CURRENTV(), CURRENTV() - 1] IS PRESENTTHEN
  sum_money[CURRENTV(), CURRENTV() - 1]
  ELSE
  0
  END,
  prev_sum_money[2010, FOR month FROM 4 TO 6 INCREMENT 1]
  = PRESENTV(sum_money[CURRENTV(), CURRENTV() - 1],
  sum_money[CURRENTV(), CURRENTV() - 1],
  0),
  prev_sum_money[2010, FOR month FROM 7 TO 9 INCREMENT 1]
  = PRESENTNNV(sum_money[CURRENTV(), CURRENTV() - 1],
  sum_money[CURRENTV(), CURRENTV() - 1],
  0),
  prev_sum_money[2010, FOR month FROM 10 TO 12 INCREMENT 1]
  = sum_money[CURRENTV(), CURRENTV() - 1]
  )
  ORDER BY
  year,
  month;
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2009   1    62806     0
  2009   2    56714    62806
  2009   3    62868    56714
  2009   4    60855    62868
  2009   5    62930    60855
  2009   6    60915    62930
  2009   7    62992    60915
  2009   8    63023    62992
  2009   9    61005    63023
  2009     10    63085    61005
  2009     11    61065    63085
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2009     12    63147    61065
  2010   1       63147
  2010   2        0
  2010   3        0
  2010   4        0
  2010   5        0
  2010   6        0
  2010   7        0
  2010   8        0
  2010   9        0
  2010     10
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2010     11
  2010     12
  已选择24行。
  测试插入几条 2010年的数据,用于验证结果.
  INSERT INTO sale_report
  SELECT TO_DATE('20100201', 'YYYYMMDD'), 'A', 20 FROM dual UNION ALL
  SELECT TO_DATE('20100501', 'YYYYMMDD'), 'A', 50 FROM dual UNION ALL
  SELECT TO_DATE('20100801', 'YYYYMMDD'), 'A', 80 FROM dual UNION ALL
  SELECT TO_DATE('20101101', 'YYYYMMDD'), 'A', 110 FROM dual;
  再次执行的结果:
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2009   1    62806     0
  2009年数据相同的,忽略...
  2009     12    63147    61065
  2010   1       63147
  2010   2    20     0
  2010   3       20
  2010   4        0
  2010   5    50     0
  2010   6       50
  2010   7        0
  2010   8    80     0
  2010   9       80
  2010     10
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2010     11   110
  2010     12      110
  已选择24行。
  IGNORE NAV
  -- 本例子用于演示 IGNORE NAV
  -- IGNORE NAV的返回值如下:
  -- 空值或缺失数字值时返回0。
  -- 空值或缺失字符串值时返回空字符串。
  -- 空值或缺失日期值时返回01-JAN-2000。
  -- 其他所有数据库类型时返回空值。
  -- KEEP NAV对空值或缺失数字值返回空值。注意默认条件下使用KEEP NAV。
  With cte AS
  (
  SELECT
  EXTRACT(YEAR FROM SALE_DATE) AS year,
  EXTRACT(MONTH FROM SALE_DATE) AS month,
  SUM(sale_money) AS sum_money,
  0 AS prev_sum_money
  FROM
  sale_report
  WHERE
  EXTRACT(YEAR FROM SALE_DATE) = 2010
  GROUP BY
  EXTRACT(YEAR FROM SALE_DATE),
  EXTRACT(MONTH FROM SALE_DATE)
  )
  SELECT
  *
  FROM cte
  MODEL IGNORE NAV
  DIMENSION BY (year, month)  -- 按照 年,月 为 维度
  MEASURES(sum_money, prev_sum_money) -- 数组的数据为 sum_money, prev_sum_money
  (
  prev_sum_money[2010, FOR month FROM 2 TO 12 INCREMENT 1]
  = sum_money[CURRENTV(), CURRENTV() - 1]
  )
  ORDER BY
  year,
  month;
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2010   2    20     0
  2010   3       20
  2010   4        0
  2010   5    50     0
  2010   6       50
  2010   7        0
  2010   8    80     0
  2010   9       80
  2010     10        0
  2010     11   110     0
  2010     12      110
  已选择11行。
  RULES UPDATE
  -- 本例子用于演示 更新已有的单元
  -- 默认情况下,如果表达式左端的引用单元存在,则更新该单元。
  -- 如果该单元不存在,就在数组中创建一条新的记录。
  -- 可以用RULES UPDATE改变这种默认的行为,指出在单元不存在的情况下不创建新纪录。
  With cte AS
  (
  SELECT
  EXTRACT(YEAR FROM SALE_DATE) AS year,
  EXTRACT(MONTH FROM SALE_DATE) AS month,
  SUM(sale_money) AS sum_money,
  0 AS prev_sum_money
  FROM
  sale_report
  WHERE
  EXTRACT(YEAR FROM SALE_DATE) = 2010
  GROUP BY
  EXTRACT(YEAR FROM SALE_DATE),
  EXTRACT(MONTH FROM SALE_DATE)
  )
  SELECT
  *
  FROM cte
  MODEL
  DIMENSION BY (year, month)  -- 按照 年,月 为 维度
  MEASURES(sum_money, prev_sum_money) -- 数组的数据为 sum_money, prev_sum_money
  RULES UPDATE
  (
  prev_sum_money[2010, FOR month FROM 2 TO 12 INCREMENT 1]
  = sum_money[CURRENTV(), CURRENTV() - 1]
  )
  ORDER BY
  year,
  month;
  YEAR  MONTH  SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2010   2    20
  2010   5    50
  2010   8    80
  2010     11   110


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-602346-1-1.html 上篇帖子: Oracle Install for linux 下篇帖子: oracle 调优3-2988729
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表