如何根据测量间隔时间计算平均值?

时间:2013-02-04 09:10:58

标签: sql sql-server-2008 tsql

我的数据类似于:

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有效期为六个月。

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

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

2 个答案:

答案 0 :(得分: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

之后你只需要将(价格*期间)的总和除以期间的总长度,然后得到答案。

这是SQL Fiddle with the calculation

同样,当你工作几个月时,你必须记住,并非所有月份都是平等的,所以十二月的价格比两月的价格更长。

答案 1 :(得分:1)

使用CTErow_number()获得每月平均值 last dateChangedFiddle-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(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