2

我有类似这样的数据:

Price   DateChanged   Product
10       2012-01-01   A
12       2012-02-01   A
30       2012-03-01   A
10       2012-09-01   A
12       2013-01-01   A
110      2012-01-01   B
112      2012-02-01   B
130      2012-03-01   B
110      2012-09-01   B
112      2013-01-01   B

我想计算平均值,但挑战是:查看第一条记录,价格 10 有效期为一个月,价格 12 有效期为一个月,价格 30 有效期为 6个月。

因此,产品 A (10+12+30+10+12)/5 的基本平均值将导致 14.8,同时考虑持续时间,则平均价格将为 ~20.1。

解决这个问题的最佳方法是什么?我知道我可以创建一个带有 row_number() 的子查询来加入以计算持续时间,但是有更好的方法吗?SQL Server 具有强大的功能,例如 STDistance,那么肯定有这个功能吗?

4

2 回答 2

2

您要查找的内容称为weighted average,并且 AFAIK,SQL Server 中没有内置函数可以为您计算它。但是,手动计算并不难。

首先,您需要找到每个数据点的权重,在这种情况下,您需要找到每个价格周期的持续时间。您的数据中可能有一些额外的列可以使查找更容易,但您也可以这样做:

SELECT p1.Product, p1.Price, p1.DateChanged AS DateStart,
       isnull(min(p2.DateChanged),getdate()) AS DateEnd
INTO #PricePlanStartEnd
FROM PricePlan p1
  LEFT OUTER JOIN PricePlan p2
    ON p1.DateChanged < p2.DateChanged
    AND p1.Product =p2.Product
GROUP BY p1.Product, p1.Price, p1.DateChanged
ORDER BY p1.Product, p1.DateChanged

这将创建一个#PricePlanStartEnd临时表,其中包含每个价格周期的开始和结束。我使用 getdate() 作为当前时间段的结束。如果您只需要计算直到最后一次价格变化的平均值,只需使用INNER JOIN而不是LEFT OUTER JOIN.

之后,您只需将 (price * period) 的总和除以期间的总长度,即可得到答案。

这是一个用于计算的 SQL Fiddle

此外,当您使用月份时,您必须记住并非所有月份都相同,因此 12 月的价格比 2 月的活跃时间更长。

于 2013-02-04T10:32:46.963 回答
1

使用CTErow_number()获得每月平均到最后一个 dateChanged小提琴演示

;with cte as (
   select product, dateChanged, price, 
          row_number() over (partition by product order by datechanged) rn
   from x
)
select t1.product, 
sum(t1.price *1.0 * datediff(month, t1.dateChanged,t2.dateChanged))/12 monthlyAvg
from cte t1  join cte t2 on t1.product = t2.product
                         and t1.rn +1 = t2.rn
group by t1.product

--Results
Product MonthlyAvg
A       20.166666
B       120.166666

或者,如果您需要最新的每日平均值,请使用LEFT JOIN Fiddle-Demo

;with cte as (
    select product, dateChanged, price, 
       row_number() over (partition by product order by datechanged) rn
    from x
)
select t1.product, 
sum(t1.price *1.0 * 
   datediff(day, t1.dateChanged,isnull(t2.dateChanged,getdate())))/365 dailyAvg
from cte t1 left join cte t2 on t1.product = t2.product
and t1.rn +1 = t2.rn
group by t1.product

--Results
product dailyAvg
A       21.386301
B       130.975342
于 2013-02-04T10:20:02.263 回答