Средняя продажа за квартал с предыдущей квартальной средней продажей

У меня есть таблица, в которой есть различные атрибуты, такие как продукт региона, год, квартал, месяц, продажа. Я должен рассчитать продажу avg_qtr для каждого продукта, имеющего тот же регион, и показать их предыдущую продажу avg_qtr. Я читал о задержке, но здесь ее невозможно использовать, поскольку она не фиксируется после того, сколько строк будет повторяться. Моя структура таблицы такая

   Region Product Year Qtr Month Sales 

    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

Я написал запрос, который вычисляет текущий QTR и показывает предыдущий средний с текущим

  WITH AvgSales
AS (SELECT
region,
product,
year,
qtr,
ROUND(AVG(sales), 2) AS avg_Sale
FROM one 
GROUP BY region,
product,
year,qtr
 )
SELECT
s.region,
s.product,
s.year,
s.month,
s.sales,
avg.qtr,
avg.avg_Sale AS Qtr_Avg_Sale,
prev.avg_sale AS Prev_Qtr_Avg_Sale
FROM one s
JOIN AvgSales avg
ON s.region = avg.region
AND s.product = avg.product
AND s.QTR = avg.qtr
AND s.year = avg.year
LEFT JOIN AvgSales prev
ON  (s.region = prev.region
AND s.product = prev.product
AND s.year - 1 = prev.year
and s.qtr=1
AND prev.qtr = 4) or
(s.region = prev.region
AND s.product = prev.product
AND s.year = prev.year
AND s.qtr - 1 = prev.qtr) ;

Я могу получить текущее среднее и предыдущее среднее этого продукта, но не наоборот. Я не уверен, как показать предыдущее среднее значение за тот квартал, в котором нет продаж в текущем квартале. Я хочу вывод, как это-

Region  Product  Year  qtr  month   sale  avg_Sale     prev_avg_sale
    NORTH     P1     2015   1   JAN     1000    2000    
    NORTH     P1     2015   1   FEB     2000    2000    
    NORTH     P1     2015   1   MAR     3000    2000    
    NORTH     P1     2015   2   APR     4000    5000            2000
    NORTH     P1     2015   2   MAY     5000    5000            2000
    NORTH     P1     2015   2   JUN     6000    5000            2000
    NORTH     P1     2015   3   JUL     7000    8000            5000
    NORTH     P1     2015   3   AUG     8000    8000            5000
    NORTH     P1     2015   3   SEP     9000    8000            5000
    NORTH     P1     2015   4   OCT     1000    2333.33         8000
    NORTH     P1     2015   4   NOV     2000    2333.33         8000
    NORTH     P1     2015   4   DEC     4000    2333.33         8000
    SOUTH     P2     2015   1   JAN     8000    6000    
    SOUTH     P2     2015   1   FEB     9000    6000    
    SOUTH     P2     2015   1   MAR     1000    6000    
    SOUTH     P2     2015   2   APR     2000    6333.33         6000
    SOUTH     P2     2015   2   MAY     8000    6333.33         6000
    SOUTH     P2     2015   2   JUN     9000    6333.33         6000
    SOUTH     P2     2015   3   JUL     1000    2333.33       6333.33
    SOUTH     P2     2015   3   AUG     2000    2333.33       6333.33
    SOUTH     P2     2015   3   SEP     4000    2333.33       6333.33
    SOUTH     P2     2015   4   OCT     5000    2666.67       2333.33
    SOUTH     P2     2015   4   NOV     2000    2666.67       2333.33
    SOUTH     P2     2015   4   DEC     1000    2666.67       2333.33
    NORTH     P3     2015   1   FEB     9000    5000    
    NORTH     P3     2015   1   FEB     1000    5000    
    NORTH     P3     2015   2   APR     2000    2000           5000
    NORTH     P3     2015   3   JUL     8000    8000           2000
    SOUTH     P3     2015   3   AUG     9000    9000    
    SOUTH     P3     2015   4   OCT     1000    2000           9000
    SOUTH     P3     2015   4   NOV     3000    2000           9000
    NORTH     P1     2016   1   JAN     2000    2000         2333.33
    NORTH     P1     2016   1   FEB     1000    2000         2333.33
    NORTH     P1     2016   1   MAR     3000    2000         2333.33
    NORTH     P2     2016   2                   2000
    SOUTH     P2     2016   1   JAN     2000    3000         2666.67
    SOUTH     P2     2016   1   JAN     4000    3000         2666.67
    SOUTH     P2     2016   2                   3000  
    SOUTH     P1     2015   1   JAN     4000    3000    
    SOUTH     P1     2015   1   JAN     2000    3000    
    SOUTH     P1     2015   1   FEB     3000    3000        

3 ответа

Решение

Вы можете использовать предложение windowing аналитической функции, если у вас есть одно упорядоченное значение для сортировки, поэтому сначала создайте DENSE_RANKing of year и qtr, затем используйте этот рейтинг в своих аналитических функциях:

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

Вышеупомянутое решение предполагает плотные квартальные данные, как указано в примере набора данных, если, однако, данные разрежены по измерению четверти, вы можете сначала уплотнить данные, как в этом запросе:

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

Это гарантирует, что для каждого года, представленного в данных, будет существовать хотя бы одна строка для каждого квартала, и, следовательно, QORD будет перечислять каждый квартал, а пробелы в данных приведут к пробелам в рассчитанных квартальных средних.

Вы также можете добиться аналогичного эффекта, изменив способ вычисления QORD, используя числовые значения YEAR и QTR, как в этом примере:

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

Здесь уплотнение не требовалось, и, тем не менее, оно по-прежнему правильно оставляет пропуски в prev_qtr_avg, но оно пропускает записи для пропущенных кварталов, которые включают уплотненные данные.

Объединяя два последних примера и добавляя в ваше новое требование для регионов, будет возвращаться или генерироваться как минимум одна строка данных за квартал, если это требуется для каждого отдельного региона, продукта и года. Оба средних значения разделены по регионам и продуктам и рассчитаны на текущий или предыдущий квартал, в зависимости от случая:

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;

Отредактировано с учетом последних требований.

Ваша проблема в том, что вы пытаетесь получить previous_avg, пытаясь манипулировать QTR и YEAR. Я использую функцию RANK, упорядочив способ сортировки данных. В соединениях я проверяю, что Средний регион = предыдущий регион, и не обращая внимания на год, так как предыдущий квартал мог быть Q4 предыдущего года для среднего года Q1; так чище.

    --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

Вам нужно присоединиться к некоторым утверждениям, которые получат среднее значение за предыдущий квартал. Вам также нужно будет объединить два оператора, потому что для кварталов 2,3,4 вы можете просто вычесть четверть в операторе соединения с предыдущим значением qtr avg, но когда это 1-й квартал, вам нужно вычесть год и установить предыдущий qtr = 4. Это утверждение должно работать для того, что вы описали.

--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;
Другие вопросы по тегам