我的数据类似于:
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,所以肯定有这个功能吗?
答案 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)
使用CTE
和row_number()
获得每月平均值 last dateChanged
。 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(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