avg sale of quarter with previous quarter avg sale

▼魔方 西西 提交于 2019-12-01 12:54:30

You can use the windowing clause of an analytic function if you have a single ordered value to sort by, so first create a DENSE_RANKing of year and qtr, then use that ranking in your analytic functions:

with t1 as ( 
  select one.*
       , dense_rank() over (order by year, qtr) qord
    from one
)
select product
     , year
     , qtr
     , month
     , sales
     , round(avg(sales) over (partition by qord),2) qtr_avg
     , round(avg(sales) over (order by qord
                              range between 1 preceding
                                        and 1 preceding),2) prev_qtr_avg
  from t1

The above solution assumes dense quarterly data as provided in the sample data set, if however, the data is sparse along the quarter dimension you can first densify the data as in this query:

with qtrs as (select level qtr from dual connect by level <=4)
, t1 as ( 
  select product
       , year
       , qtrs.qtr
       , month
       , sales
       , dense_rank() over (order by year, qtrs.qtr) qord
    from qtrs
    left outer join one partition by (year)
      on one.qtr = qtrs.qtr
)
select product
     , year
     , qtr
     , month
     , sales
     , round(avg(sales) over (partition by qord),2) qtr_avg
     , round(avg(sales) over (order by qord
                              range between 1 preceding
                                        and 1 preceding),2) prev_qtr_avg
  from t1

This ensures that for every year represented in the data at least one row will exist for each quarter, and consequently QORD will enumerate every quarter, and gaps in the data will result in gaps in the calculated quarterly averages.

You can also achieve a similar effect by altering the way QORD is calculated by exploiting the numeric natures of YEAR and QTR as in this example:

with t1 as (select one.*, year*4+qtr qord from one)
select product
     , year
     , qtr
     , month
     , sales
     , round(avg(sales) over (partition by qord),2) qtr_avg
     , round(avg(sales) over (order by qord
                              range between 1 preceding
                                        and 1 preceding),2) prev_qtr_avg
  from t1

Here no densification was required, and yet it still correctly leaves gaps in the prev_qtr_avg, but it does leave out records for missing quarters which the densified data includes.

Combining the last two examples, and adding in your new requirement for regions a at least one row of data per quarter will be returned or generated if required for every distinct region, product and year. Both averages are partitioned by region and product and calculated per current or previous quarter as the case may be:

with qtrs(qtr) as (select level from dual connect by level <= 4)
, t1 as (
select region, product, year, q.qtr, month, sales, year*4+q.qtr qord
  from qtrs q
  left join one partition by (region, product, year)
    on q.qtr = one.qtr
)
select region
     , product
     , year
     , qtr
     , month
     , sales
     , round(avg(sales) over (partition by region, product, qord),2) avg_sale
     , round(avg(sales) over (partition by region, product
                              order by qord
                              range between 1 preceding
                                        and 1 preceding),2) prev_avg_sale
  from t1
 order by year, region, qtr, product;

Edited with the latest requirements.

Your problem is that you are trying to get the previous_avg by trying to manipulate QTR and YEAR. I'm using the RANK function, ordering the way I want the data ranked. In the joins I'm making sure that Average Region = Previous Region and disregarding year since the previous quarter could be Q4 of the Previous year for Average year Q1; it's cleaner this way.

    --Build the test table
    IF OBJECT_ID('SALES','U') IS NOT NULL
        DROP TABLE SALES

    CREATE TABLE SALES
    (
          Region  VARCHAR(255)
        , Product VARCHAR(10)
        , [Year]  INT
        , QTR     INT
        , [Month] VARCHAR(19)
        , Sales   DECIMAL(19,4)
    );

    INSERT SALES
    VALUES
         ('NORTH', 'P1', 2015, 1, 'JAN', 1000)
        ,('NORTH', 'P1', 2015, 1, 'FEB', 2000)
        ,('NORTH', 'P1', 2015, 1, 'MAR', 3000)
        ,('NORTH', 'P1', 2015, 2, 'APR', 4000)
        ,('NORTH', 'P1', 2015, 2, 'MAY', 5000)
        ,('NORTH', 'P1', 2015, 2, 'JUN', 6000)
        ,('NORTH', 'P1', 2015, 3, 'JUL', 7000)
        ,('NORTH', 'P1', 2015, 3, 'AUG', 8000)
        ,('NORTH', 'P1', 2015, 3, 'SEP', 9000)
        ,('NORTH', 'P1', 2015, 4, 'OCT', 1000)
        ,('NORTH', 'P1', 2015, 4, 'DEC', 4000)
        ,('NORTH', 'P1', 2015, 4, 'NOV', 2000)
        ,('NORTH', 'P3', 2015, 1, 'FEB', 1000)
        ,('NORTH', 'P3', 2015, 1, 'FEB', 9000)
        ,('NORTH', 'P3', 2015, 2, 'APR', 2000)
        ,('NORTH', 'P3', 2015, 3, 'JUL', 8000)
        ,('NORTH', 'P1', 2016, 1, 'MAR', 3000)
        ,('NORTH', 'P1', 2016, 1, 'FEB', 1000)
        ,('NORTH', 'P1', 2016, 1, 'JAN', 2000)
        ,('SOUTH', 'P1', 2015, 1, 'JAN', 2000)
        ,('SOUTH', 'P1', 2015, 1, 'FEB', 3000)
        ,('SOUTH', 'P1', 2015, 1, 'JAN', 4000)
        ,('SOUTH', 'P2', 2015, 1, 'MAR', 1000)
        ,('SOUTH', 'P2', 2015, 1, 'JAN', 8000)
        ,('SOUTH', 'P2', 2015, 1, 'FEB', 9000)
        ,('SOUTH', 'P2', 2015, 2, 'JUN', 9000)
        ,('SOUTH', 'P2', 2015, 2, 'MAY', 8000)
        ,('SOUTH', 'P2', 2015, 2, 'APR', 2000)
        ,('SOUTH', 'P2', 2015, 3, 'SEP', 4000)
        ,('SOUTH', 'P2', 2015, 3, 'AUG', 2000)
        ,('SOUTH', 'P2', 2015, 3, 'JUL', 1000)
        ,('SOUTH', 'P2', 2015, 4, 'NOV', 2000)
        ,('SOUTH', 'P2', 2015, 4, 'DEC', 1000)
        ,('SOUTH', 'P2', 2015, 4, 'OCT', 5000)
        ,('SOUTH', 'P3', 2015, 3, 'AUG', 9000)
        ,('SOUTH', 'P3', 2015, 4, 'OCT', 1000)
        ,('SOUTH', 'P3', 2015, 4, 'NOV', 3000)
        ,('SOUTH', 'P2', 2016, 1, 'JAN', 2000)
        ,('SOUTH', 'P2', 2016, 1, 'JAN', 4000);


    --CTE TO CAPTURE AVG SALES BY REGION, PRODCUT, YEAR, QTR;  OMIT PRODUCT IF YOU WANT STRAIGHT UP QUARTER AVG, REGARDLESS OF PRODCUCT
    WITH cteAvgSales AS
    (
        SELECT Region, Product, [Year], QTR, AVG(Sales) current_avg
            , RANK() OVER(ORDER BY Region, Product, [Year], QTR) AS RNK
        FROM SALES
        GROUP BY Region, Product, [Year], QTR
    )
    SELECT s.Region, s.Product, s.[Year] AS [year], s.QTR AS [quarter], s.[Month], s.Sales, a.current_avg, p.current_avg AS previous_avg
    FROM SALES s
        INNER JOIN cteAvgSales a ON a.Region = s.Region
            AND a.Product = s.Product
            AND a.[Year]  = s.[Year]
            AND a.QTR = s.QTR
        LEFT JOIN cteAvgSales p ON p.Region = a.Region
            AND p.Product = s.Product
            AND p.RNK=a.RNK-1
    ORDER BY s.Region, s.Product, s.[Year], s.QTR

You need to join on some sub-select statements that will get the previous quarter avg. You will also need to do a union of two statements because for quarters 2,3,4 you can simply subtract a quarter on the join statement to the previous qtr avg, but when it is the 1st quarter you need to subtract a year and set the previous qtr = 4. This statement should work for what you have described.

--handles when the current quarter being viewed is 2,3,or 4 because those would still be in the same year when looking at the previous quarter
select  t1.product,
        t1.year,
        t1.month,
        t1.sales ,
        t1.qtr,
        round(avg(t1.sales) over (partition by t1.qtr,t1.year),2) as av,
        t2.prev_av
from one t1
left join ( select
                product,
                year,
                month,
                sales ,
                qtr,
                round(avg(sales) over (partition by qtr,year),2) as prev_av
            from one
            ) t2
on t1.year = t2.year
and (t1.qtr - 1) = t2.qtr
where t1.qtr in (2,3,4)
union
--handles the 1st quarter of the year when you need to grab the 4th quarter of the previous year for the previous avg
select  t3.product,
        t3.year,
        t3.month,
        t3.sales ,
        t3.qtr,
        round(avg(t3.sales) over (partition by t3.qtr,t3.year),2) as av,
        t4.prev_av
from one t3
left join ( select
                product,
                year,
                month,
                sales,
                qtr,
                round(avg(sales) over (partition by qtr,year),2) as prev_av
            from one
            ) t4
on (t3.year - 1) = t4.year
and t4.qtr = 4
where t3.qtr = 1;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!