【问题标题】:Dynamic average calculation动态平均计算
【发布时间】:2022-12-09 06:01:47
【问题描述】:

我想添加一个平均成本列来计算不同时间段的平均值。

因此,在下面的示例中,有 6 个月的成本,第一列找到所有 6 个月的平均值,即 average(1,5,8,12,15,20)

下一个“Half Period”列确定有多少个总周期并计算最近 3 个周期的平均值,即 average(12,15,20)

第一个平均值很简单,例如

AVG(COST)

我在半年内尝试过的是:

AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN x PRECEDING AND CURRENT ROW)

x当然是一个整数值,我该如何编写自动输入所需整数的语句呢?即在此示例中,6 个周期需要平均 3 行,因此 x=2。

x 可以通过一些子查询找到,例如

SELECT ( CEILING(COUNT(PERIOD) / 2) - 1) FROM TABLE

示例表:

Period Cost
Jan 1
Feb 5
Mar 8
Apr 12
May 15
Jun 20

期望的输出:

Period Cost All Time Average Cost Half Period Average Cost
Jan 1 10.1 1
Feb 5 10.1 3
Mar 8 10.1 4.7
Apr 12 10.1 8.3
May 15 10.1 11.7
Jun 20 10.1 15.7

【问题讨论】:

  • x 当然是一个整数值,这与样本数据中的 1 月、2 月等有什么关系?给定的营业年不一定与日历年相同?

标签: sql sql-server ssms


【解决方案1】:

这里的主要问题是你不能使用多变的或一个表达对于行数前言在窗口表达式中,我们必须在以下内容中使用 x 的文字值:

BETWEEN x PRECEDING

如果句点数量有限,那么我们可以使用 CASE 语句在可能的表达式之间切换:

CASE
    WHEN CEILING(COUNT(PERIOD) / 2) - 1 <= 1 
        THEN AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
    WHEN CEILING(COUNT(PERIOD) / 2) - 1 <= 2 
        THEN AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
    WHEN CEILING(COUNT(PERIOD) / 2) - 1 <= 3 
        THEN AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)
    WHEN CEILING(COUNT(PERIOD) / 2) - 1 <= 4 
        THEN AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN 4 PRECEDING AND CURRENT ROW)
    WHEN CEILING(COUNT(PERIOD) / 2) - 1 <= 5 
        THEN AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN 5 PRECEDING AND CURRENT ROW)
    WHEN CEILING(COUNT(PERIOD) / 2) - 1 <= 6 
        THEN AVG(COST) OVER (ORDER BY PERIOD ROWS BETWEEN 6 PRECEDING AND CURRENT ROW)
END as [Half Period Average Cost]

【讨论】:

  • 谢谢,这会奏效。不幸的是,对于大量可能的周期来说,这是一个繁琐的解决方案,有些可能高达 30 个。
  • 我们只看到了一半的图片,发布一个更大的数据集来处理会带来更好质量的答案,你的最终结果可能是一个折衷方案,涉及一个真正的滚动平均值而不是固定桶方法,很难想象你的数据可能有什么限制
【解决方案2】:

我在 SQL 中添加了这一步。但是我的窗口函数拒绝接受变量 half_period_rounded。所以我们还没有到那一步。 :-)

SQL query

【讨论】:

    【解决方案3】:

    这看起来像是偷偷摸摸的窗口函数聚合的工作!

    DECLARE @TABLE TABLE (SaleID INT IDENTITY, Cost DECIMAL(12,4), SaleDateTime DATETIME)
    INSERT INTO @TABLE (SaleDateTime, Cost) VALUES
    ('2022-Jan-01', 1 ),
    ('2022-Feb-01', 5 ),
    ('2022-Mar-01', 8 ),
    ('2022-Apr-01', 12),
    ('2022-May-01', 15),
    ('2022-Jun-01', 20)
    
    
    SELECT DISTINCT DATEPART(YEAR,SaleDateTime) AS Year, DATEPART(MONTH,SaleDateTime) AS MonthNumber, DATENAME(MONTH,SaleDateTime) AS Month,
           AVG(Cost) OVER (ORDER BY (SELECT 1)) AS AllTimeAverage,
           AVG(Cost) OVER (PARTITION BY DATEPART(YEAR,SaleDateTime), DATEPART(MONTH, SaleDateTime) ORDER BY SaleDateTime) AS MonthlyAverage, 
           AVG(Cost) OVER (PARTITION BY DATEPART(YEAR,SaleDateTime), DATEPART(QUARTER,SaleDateTime) ORDER BY SaleDateTime) AS QuarterlyAverage,
           AVG(Cost) OVER (PARTITION BY CASE WHEN SaleDateTime BETWEEN CAST(DATEADD(MONTH,-1,DATEADD(DAY,1-DATEPART(DAY,SaleDateTime),SaleDateTime)) AS DATE) 
                                              AND CAST(DATEADD(MONTH,2,DATEADD(DAY,1-DATEPART(DAY,SaleDateTime),SaleDateTime)) AS DATE) 
                                             THEN 1 END ORDER BY SaleDateTime) AS RollingThreeMonthAverage
     FROM @TABLE
     ORDER BY DATEPART(YEAR,SaleDateTime), DATEPART(MONTH,SaleDateTime)
    

    我们在这里作弊,让 case 表达式在我们的 3 个月滚动窗口中找到我们想要的行。我选择将其保留在上个月、本月和下个月的滚动窗口(从上个月的第一天到下个月的最后一天 - '2022-01-01 00:00:00' 到2 月的 '2022-04-01 00:00:00')。

    对整个结果集、月份和季度进行分区很简单,但是当您将滚动的三个月转换为描述它的 case 表达式时并不会复杂多少。

    Year    MonthNumber Month       AllTimeAverage  MonthlyAverage  QuarterlyAverage    RollingThreeMonthAverage
    --------------------------------------------------------------------------------------------------------
    2022    1           January     10.166666       1.000000        1.000000            1.000000
    2022    2           February    10.166666       5.000000        3.000000            3.000000
    2022    3           March       10.166666       8.000000        4.666666            4.666666
    2022    4           April       10.166666       12.000000       12.000000           6.500000
    2022    5           May         10.166666       15.000000       13.500000           8.200000
    2022    6           June        10.166666       20.000000       15.666666           10.166666
    

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 2022-11-26
      • 2015-05-03
      • 2014-11-18
      相关资源
      最近更新 更多