Oracle Reporting 4 - Time Series Calculations

Query 2: Use lag function to put sales of both year 2000 and 2001 in one row:
WITH v AS
(
SELECT SUBSTR(p.Prod_Name,1,6) Prod, t.Calendar_Year,
sum(s.amount_sold) sales
FROM Sales s, Times t, Products p
WHERE s.Time_id = t.Time_id AND
s.Prod_id = p.Prod_id AND p.Prod_name in ('Y Box') AND
t.Calendar_Year in (2000,2001)       
GROUP BY p.Prod_Name, t.Calendar_Year
)
select * from (
select prod, calendar_year, sales, lag(sales,1) over(partition by v.prod order by v.calendar_year) last_year_sales
from v
)
where calendar_year=2001;

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        146  consistent gets
          0  physical reads
          0  redo size
        761  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

Query 2_1: Alternative, user could use decode to generate the same output. This is a well-known method to do row to column transformation.
select p.prod_name, sum(decode(t.calendar_year,2000, amount_sold,0)) "2000_sales",sum(decode(t.calendar_year,2001, amount_sold,0)) "2001_sales"
from sales s, times t, products p where s.time_id=t.time_id and s.prod_Id=p.prod_id
and t.calendar_year in (2000, 2001) and p.prod_name in ('Y Box') group by p.prod_name;


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        146  consistent gets
          0  physical reads
          0  redo size
        687  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)

          1  rows processed


Autotrace estimates Query_2 and Query_2_1 have same performance.


What if the customer needs all years sales in one row? I could add another column using same method, either decode or lag. However, there's an elegant way to do this, using Pivot operation.

Query 2_3 :
WITH v AS
(
SELECT SUBSTR(p.Prod_Name,1,6) Prod, t.Calendar_Year,
sum(s.amount_sold) amount_sold
FROM Sales s, Times t, Products p
WHERE s.Time_id = t.Time_id AND
s.Prod_id = p.Prod_id AND p.Prod_name in ('Y Box')
GROUP BY p.Prod_Name, t.Calendar_Year
order by t.calendar_year
)
select * from
(select prod, calendar_year, amount_sold
from v) s pivot( sum(amount_sold)
for calendar_year in (1998 "1998_sales", 1999 "1999_sales",2000 "2000_sales", 2001 "2001_sales"));


Query 3: A data densified query, compares weekly cumulative sales of 2001 to that of 2000. 也就是常说的同比。在这里用PARTITION BY (v.Product_Name) RIGHT OUTER JOIN 语句与Times这个维度表做右外连接,从而得到一个在时间维度上数据都”填充“完整的结果集。这个SQL还用了上面提到的LAG函数实现行转列。从而最终得到2000和2001同比year_to_date销售额对比
WITH v AS  
(  
SELECT SUBSTR(p.Prod_Name,1,6) Prod, t.Calendar_Year Year,  
t.Calendar_Week_Number Week, SUM(Amount_Sold) Sales  
FROM Sales s, Times t, Products p  
WHERE s.Time_id = t.Time_id AND  
s.Prod_id = p.Prod_id AND 
p.Prod_name in ('Y Box') AND  
t.Calendar_Year in (2000,2001) AND  
t.Calendar_Week_Number BETWEEN 30 AND 40  
GROUP BY p.Prod_Name, t.Calendar_Year, t.Calendar_Week_Number  
)  
SELECT Prod , Year, Week, Sales, Weekly_ytd_sales, Weekly_ytd_sales_prior_year  
FROM (  
  SELECT Prod, Year, Week, Sales, Weekly_ytd_sales,  
    LAG(Weekly_ytd_sales, 1) OVER (PARTITION BY Prod , Week ORDER BY Year) Weekly_ytd_sales_prior_year  
  FROM (
    SELECT v.Prod Prod , t.Year Year, t.Week Week, NVL(v.Sales,0) Sales,  
    SUM(NVL(v.Sales,0)) OVER (PARTITION BY v.Prod , t.Year ORDER BY t.week) weekly_ytd_sales  
    FROM v  
    PARTITION BY (v.Prod )  
    RIGHT OUTER JOIN  
    (SELECT DISTINCT Calendar_Week_Number Week, Calendar_Year Year  
     FROM Times  
     WHERE Calendar_Year IN (2000, 2001)) t  
    ON (v.week = t.week AND v.Year = t.Year)  
) dense_sales  
) year_over_year_sales  
WHERE Year = 2001 AND Week BETWEEN 30 AND 32  
ORDER BY prod,year, week;

Partition out join 与普通外连接的不同之处在于实现了数据填充,它的结果集必然要比普通外连接要大。如下例子:

CREATE TABLE inventory (
time_id DATE,
product VARCHAR2(10),
quantity NUMBER);
INSERT INTO inventory VALUES (TO_DATE('01/04/01', 'DD/MM/YY'), 'bottle', 10);
INSERT INTO inventory VALUES (TO_DATE('06/04/01', 'DD/MM/YY'), 'bottle', 10);
INSERT INTO inventory VALUES (TO_DATE('01/04/01', 'DD/MM/YY'), 'can', 10);
INSERT INTO inventory VALUES (TO_DATE('04/04/01', 'DD/MM/YY'), 'can', 10);
commit;
可以看到产品销售在时间维度上是稀疏的,只有4月1日和4月6月有销量。Partition outer join能够实现数据在右表维度上的完整填充,在这个例子是时间维度。实际上就是去重后的product和time_id这两个维度上的笛卡尔积。

SELECT times.time_id, product, nvl(quantity,0) FROM inventory
PARTITION BY (product)
RIGHT OUTER JOIN times ON (times.time_id = inventory.time_id)
WHERE times.time_id BETWEEN TO_DATE('01/04/01', 'DD/MM/YY') AND TO_DATE('06/04/01', 'DD/MM/YY')
ORDER BY product,time_id;

TIME_ID   PRODUCT    NVL(QUANTITY,0)
--------- ---------- ---------------
01-APR-01 bottle                  10
01-APR-01 bottle                  10
02-APR-01 bottle                   0
03-APR-01 bottle                   0
04-APR-01 bottle                   0
05-APR-01 bottle                   0
06-APR-01 bottle                  10
06-APR-01 bottle                  10
01-APR-01 can                     10
01-APR-01 can                     10
02-APR-01 can                      0
03-APR-01 can                      0
04-APR-01 can                     10
04-APR-01 can                     10
05-APR-01 can                      0
06-APR-01 can                      0
我们可以看一下普通的外连接的结果:
SELECT times.time_id, product, nvl(quantity,0) FROM inventory
RIGHT OUTER JOIN times ON (times.time_id = inventory.time_id)
WHERE times.time_id BETWEEN TO_DATE('01/04/01', 'DD/MM/YY') AND TO_DATE('06/04/01', 'DD/MM/YY')
ORDER BY product,time_id;
TIME_ID   PRODUCT    NVL(QUANTITY,0)
--------- ---------- ---------------
01-APR-01 bottle                  10
01-APR-01 bottle                  10
06-APR-01 bottle                  10
06-APR-01 bottle                  10
01-APR-01 can                     10
01-APR-01 can                     10
04-APR-01 can                     10
04-APR-01 can                     10
02-APR-01                          0
03-APR-01                          0
05-APR-01                          0



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值