欲忘树 发表于 2018-9-26 12:16:23

Oracle的 MODEL 查询

  首先先造测试表/测试数据
  SQL> CREATE TABLE SALE_REPORT (
  2   SALE_DATEDATE NOT NULL ,
  3   SALE_ITEMVARCHAR(2) NOT NULL ,
  4   SALE_MONEY DECIMAL(10,2) NOT NULL
  5)
  6/
  Table created.
  SQL> DECLARE
  2    v_begin_dayDATE;
  3    v_end_dayDATE;
  4BEGIN
  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;
  14END;
  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
  -- ----------
  0162806
  0256714
  0362868
  0460855
  0562930
  0660915
  0762992
  0863023
  0961005
  1063085
  1161065
  MO SUM_MONEY
  -- ----------
  1263147
  已选择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
  -- ----------
  0162806
  0256714
  0362868
  0460855
  0562930
  0660915
  0762992
  0863023
  0961005
  1063085
  1161065
  MO SUM_MONEY
  -- ----------
  1263147
  Q1    182388
  Q2    184700
  Q3    187020
  Q4    187297
  YY    741405
  已选择17行。
  先把上面的 MODEL 的部分说明一下:
  MODEL关键字 必须
  DIMENSION BY 维度 必须
  MEASURES 指定作为数组的列 必须
  RULES 对数组进行各种操作的描述 可选
  -- 上一个SQL, 用 + 用的太多了。
  -- 这里用 SUM() 返回特定范围内的数据单元
  -- 这里用 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),
  sum_money['Q2'] = SUM(sum_money),
  sum_money['Q3'] = SUM(sum_money),
  sum_money['Q4'] = SUM(sum_money),
  sum_money['YY'] = SUM(sum_money)
  )
  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
  = sum_money
  )
  ORDER BY
  month;
  MONTH SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- --------------
  162806      0
  256714   62806
  362868   56714
  460855   62868
  562930   60855
  660915   62930
  762992   60915
  863023   62992
  961005   63023
  1063085   61005
  1161065   63085
  MONTH SUM_MONEY PREV_SUM_MONEY
  ---------- ---------- --------------
  1263147   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
  = sum_money
  )
  ORDER BY
  month;
  YEARMONTHSUM_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
  YEARMONTHSUM_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
  = sum_money,
  prev_sum_money = sum_money,
  prev_sum_money
  = CASE WHEN sum_money IS PRESENTTHEN
  sum_money
  ELSE
  0
  END,
  prev_sum_money
  = PRESENTV(sum_money,
  sum_money,
  0),
  prev_sum_money
  = PRESENTNNV(sum_money,
  sum_money,
  0),
  prev_sum_money
  = sum_money
  )
  ORDER BY
  year,
  month;
  YEARMONTHSUM_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
  YEARMONTHSUM_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
  YEARMONTHSUM_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;
  再次执行的结果:
  YEARMONTHSUM_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
  YEARMONTHSUM_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
  = sum_money
  )
  ORDER BY
  year,
  month;
  YEARMONTHSUM_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
  = sum_money
  )
  ORDER BY
  year,
  month;
  YEARMONTHSUM_MONEY PREV_SUM_MONEY
  ---------- ---------- ---------- --------------
  2010   2    20
  2010   5    50
  2010   8    80
  2010   11   110

页: [1]
查看完整版本: Oracle的 MODEL 查询