avg sale of quarter with previous quarter avg sale

前端 未结 3 2010
北荒
北荒 2021-01-16 08:09

I have a table one in which there are various attribute like region product,year,qtr,month,sale. I have to calculate the avg_qtr sale of each product having same region and

相关标签:
3条回答
  • 2021-01-16 08:50

    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;
    
    0 讨论(0)
  • 2021-01-16 08:51

    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
    
    0 讨论(0)
  • 2021-01-16 09:08

    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;
    
    0 讨论(0)
提交回复
热议问题