在MySQL中,我试图从一个简单的价格表中计算2个运行平均值。下表中有日期和价格。
Pricedate Price
01/10/2018 1
28/09/2018 3
27/09/2018 5
26/09/2018 4
25/09/2018 8
24/09/2018 4
21/09/2018 6
20/09/2018 2
19/09/2018 1
18/09/2018 0
对于每个日期,我想计算移动的2天和5天的平均值。
Pricedate Price Average2 Average5
01/10/2018 1 4 4
28/09/2018 3 7 5
27/09/2018 5 7 5
26/09/2018 4 10 5
25/09/2018 8 8 4
24/09/2018 4 8 3
21/09/2018 6 5 2
20/09/2018 2 2 1
19/09/2018 1 1 0
18/09/2018 0 0 0
我尝试了以下操作,但是我没有正确返回平均值。
SELECT *, DATE_SUB(Pricedate, INTERVAL 5 DAY_HOUR) AS Days, AVG(price)
AS Average2
FROM prices
GROUP BY Days
答案 0 :(得分:0)
以下是三种方法:
第一个,干净,简单但缓慢的解决方案:
SELECT twoday.pricedate,twoday.price,twoday.ravg as twoday,fiveday.ravg as fiveday
FROM (
SELECT a.Pricedate,a.Price, SUM(b.Price)/2 as ravg
from table1 a
LEFT join table1 b on b.Pricedate between date_SUB(a.Pricedate, interval 2 day) AND a.Pricedate
GROUP BY a.Pricedate
ORDER BY a.Pricedate
) twoday
JOIN (
SELECT a.Pricedate,SUM(b.Price)/5 AS ravg //
FROM table1 a
LEFT JOIN table1 b ON b.Pricedate BETWEEN DATE_SUB(a.Pricedate, INTERVAL 5 DAY) AND a.Pricedate
GROUP BY a.Pricedate
ORDER BY a.Pricedate
) fiveday ON fiveday.pricedate=twoday.pricedate
基本上,我们在此操作是针对原始表的每一行,我们再次连接该表的N行,其中N是我们正在使用的时间窗口。然后我们将它们重新组合在一起以计算合计值。
第二种方法是使用缓冲区,但是由于mysql中没有数组,因此我们需要使用多个变量。它看起来很丑,但是速度更快,因为我们只选择了每一行。
SELECT Pricedate,Price,twoday,fiveday
FROM (
SELECT Pricedate,Price,
@5dayago:= @4dayago,
@4dayago:= @3dayago,
@3dayago:= @2dayago,
@2dayago:= @1dayago,
@1dayago:= a.Price,
(@2dayago + @1dayago)/2 AS twoday,
(@5dayago + @4dayago + @3dayago + @2dayago + @1dayago)/5 AS fiveday
FROM table1 a,
(SELECT @5dayago:=5, @4dayago:=0, @3dayago:=0, @2dayago:=0, @1dayago:=0) b
ORDER BY a.Pricedate
) c
还请注意,这种方法无法正确处理跳过的日子
第三种方法是在后端/端点执行此操作,这是php中的示例:
$conn = new mysqli($servername, $username, $password, $dbname);
$buffer=array_fill(0, 5, 0);
$sql = "SELECT Pricedate,Price FROM table1 ORDER BY Pricedate";
$result = $conn->query($sql);
$data=[];
$lastdate=null;
while($row = $result->fetch_assoc()) {
if($lastdate===null)
$lastdate=new DateTimeImmutable($row["Pricedate"]);
else{
//try to fill buffer with zeroes for every missing day
$newdate=new DateTimeImmutable($row["Pricedate"]);
$interval=$lastdate->diff($newdate)->days;
for($i=1;$i<$interval;$i++){
array_unshift($buffer,0);
array_pop($buffer);
}
$lastdate=$newdate;
}
array_unshift($buffer,$row["Price"]); //add new price into buffer
array_pop($buffer); //and throw out the oldest one
$row["fivedays"]=array_sum($buffer)/5;
$row["twodays"]=array_sum(array_slice($buffer,0,2))/2;
$data[] = $row;
}
print_r($data);