【问题标题】:Exclude values in a rolling average (dropping the high and low values) partition by clause排除滚动平均值中的值(删除高值和低值)按子句分区
【发布时间】:2021-09-11 22:38:57
【问题描述】:

我正在尝试计算 6 周内的滚动平均值,其中我在特定时间范围内放弃了高销售周和低销售周。

我使用 windows 函数来确定销售高峰和低谷周,但我无法在 6 周窗口内运行平均值并排除高低。

我尝试在 avg 函数中使用 case 语句,但它返回了错误的结果。

这是我的代码:

;with average_daily_sales
as
(
select sum(SalesUnits) as total_sales_units, 
ItemNumber, 
Store, 
END_OF_WEEK,
BEGIN_OF_WEEK
from CONFORM_MOVEMENT
where Store = 10 and ItemNumber =1026295
group by ItemNumber, Store, END_OF_WEEK, BEGIN_OF_WEEK


)

--USING windows function to accomplish a 6 week rolling average to identify the high and low selling weeks
--PARTITION BY creates pairs of stores and item numbers to analyze per each window (6 records represent 6 weeks)
--Set the window being analyzed by replacing the integer value between "ROWS BETWEEEN N PRECEEDING"
,highs_lows_identifier
as
(

select
max(total_sales_units) 
    over (PARTITION BY Store, ItemNumber ORDER BY END_OF_WEEK
            ROWS BETWEEN 5 PRECEDING AND CURRENT ROW ) as highs,
min(total_sales_units) 
    over (PARTITION BY Store, ItemNumber ORDER BY END_OF_WEEK
            ROWS BETWEEN 5 PRECEDING AND CURRENT ROW ) as lows,
Store,
ItemNumber,
END_OF_WEEK,
BEGIN_OF_WEEK,
total_sales_units
from average_daily_sales
group by Store, ItemNumber, END_OF_WEEK, BEGIN_OF_WEEK, total_sales_units


)
--Remove highs and lows from their respective record
,remove_highs_and_lows
as
(
select          
        avg(case when total_sales_units = highs 
                        or total_sales_units = lows
                        then null else total_sales_units end) 
            over ( partition by Store, ItemNumber ORDER BY END_OF_WEEK
                    rows between 5 preceding and current row) as average_sales_units,

        Store,
        ItemNumber,
        BEGIN_OF_WEEK,
        END_OF_WEEK,
        highs,
        lows,
        total_sales_units,
        total_sales_units /7 as daily_sales_units
from highs_lows_identifier
)

select * from remove_highs_and_lows
order by END_OF_WEEK asc

结果图片:

预期:记录 19 中的 average_sales_units 应为 61.5(不包括记录 16 和 17)。但是,没有发生排除,我的结果是 64。这个逻辑应该出现在每条记录中(例如记录 18 average_sales_units 应该排除记录 16 和 13)。

任何建议都会很棒!

谢谢

【问题讨论】:

  • 样本数据和期望的结果真的很有帮助。
  • 嗨@GordonLinoff 我应该附上一个包含数据和结果的excel文件吗?抱歉,最后的图片和解释就足够了
  • 。 .不,您应该将几行示例数据作为文本表添加到问题中。
  • 根据问题指南,请不要发布代码、数据、错误消息等的图像 - 将文本复制或输入到问题中。请保留将图像用于图表或演示渲染错误,无法通过文本准确描述的事情。

标签: sql sql-server window-functions rolling-computation


【解决方案1】:

最简单的方法大概是apply。这对数据的外观做了一些假设,但想法是:

select cm.*, rolling_avg_sales
from CONFORM_MOVEMENT cm outer apply
     (select avg(cm2.sales) as rolling_avg_sales
      from (select cm2.*,
                   row_number() over (order by sales desc) as seqnum_asc,
                   row_number() over (order by sales desc) as seqnum_desc
            from CONFORM_MOVEMENT cm2
            where cm2.store = cm.store and cm2.item = cm.item and
                  cm2.end_of_week <= cm.end_of_week and
                  cm2.end_of_week > dateadd(week, -5, cm.end_of_week)
           ) cm2
      where 1 not in (seqnum_desc, seqnum_asc)
     ) cm2

【讨论】:

    【解决方案2】:

    谢谢 Gordon - 再次为没有提供示例数据而道歉,但你让我感动,我想我把这个解决方案复杂化了。

    鉴于我可以使用我的窗口函数识别滚动 6 周中的高点和低点,我可以简单地将滚动 6 周的总和减去高点和低点,除以 4 得到每周平均值,然后再除以 7 得到获取每日平均值。

    为了遵守业务逻辑,我将忽略前 6 周,因为前几周没有 6 周的时间跨度可供使用。

    这里是:

    --Remove highs and lows from their respective record - subtracting highs and lows to get the average
    ,remove_highs_and_lows
    as
    (
    select
        --case
        --  when highs = lows then END_OF_WEEK
        --  when highs = total_sales_units 
        --      or lows = total_sales_units then END_OF_WEEK else null end as is_highs_lows,
            
            ((sum(total_sales_units)
                over ( partition by Store, ItemNumber ORDER BY END_OF_WEEK
                        rows between 5 preceding and current row) - highs - lows) / 4)
                        / 7 as daily_sales_units,
            sum(total_sales_units)
                over ( partition by Store, ItemNumber ORDER BY END_OF_WEEK
                        rows between 5 preceding and current row) as total_weekly_sales_units,
            (sum(total_sales_units)
                over ( partition by Store, ItemNumber ORDER BY END_OF_WEEK
                        rows between 5 preceding and current row) - highs - lows) / 4 as avg_weekly_sales_units,
            total_sales_units,
            highs,
            lows,
            Store,
            ItemNumber,
            BEGIN_OF_WEEK,
            END_OF_WEEK
    from highs_lows_identifier
    
    )
    
    select * from remove_highs_and_lows
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-24
      • 1970-01-01
      • 2019-11-14
      • 2020-03-16
      • 1970-01-01
      • 2014-11-29
      相关资源
      最近更新 更多