为每一行选择x天的AVG

时间:2018-10-05 12:57:05

标签: mysql

在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

1 个答案:

答案 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);