MySQL对使用CASE WHENs的许多SUM()求平均值 - 有更好的方法吗?

时间:2013-05-31 23:58:25

标签: mysql sql performance sum average

有没有更好的方法来处理这种情况?

我有一张桌子,每5分钟的“时间”块有几行“速度”。我想在这些5分钟的12个区块中取平均值来获得每小时的速度。我想出的虚拟方法是计算每个5分钟块的总和,然后将它们除以12。

有没有更好的方法,无论是编码简单还是sql效率?

SELECT (

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
10 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
5 MINUTE) THEN speed ELSE 0 END) + 

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
15 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
10 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
20 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
15 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
25 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
20 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
30 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
25 MINUTE) THEN speed ELSE 0 END) + 

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
35 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
30 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
40 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
35 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
45 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
40 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
50 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
45 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
55 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
50 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
60 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
55 MINUTE) THEN speed ELSE 0 END) +

SUM(CASE WHEN time > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
65 MINUTE) AND time < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 
60 MINUTE) THEN speed ELSE 0 END)

)/12
FROM table

谢谢!

1 个答案:

答案 0 :(得分:1)

SELECT AVG(IFNULL(ws.sums, 0))
FROM 
(
  SELECT 1 i union all SELECT 2 union all SELECT 3 union all SELECT 4 union all SELECT 5 union all SELECT 6 union all SELECT 7 union all SELECT 8 union all SELECT 9 union all SELECT 10 union all SELECT 11 union all SELECT 12
) windows
LEFT JOIN
(
  SELECT SUM(speed) sums,
         FLOOR(TIME_TO_SEC(TIMEDIFF(CURRENT_TIMESTAMP, time)) / 300) window
  FROM workers_speeds
  WHERE TIME_TO_SEC(TIMEDIFF(CURRENT_TIMESTAMP, time)) / 60 < 65
  AND TIME_TO_SEC(TIMEDIFF(CURRENT_TIMESTAMP, time)) / 60 > 5
  GROUP BY FLOOR(TIME_TO_SEC(TIMEDIFF(CURRENT_TIMESTAMP, time)) / 300)
) ws on windows.i = ws.window

Click here to see it in action at SQL Fiddle

说明

首先查看底部内部查询...我们需要一种识别每个间隔的简单方法。为此,我使用TIMEDIFF()函数来获取time列与当前时间之间的差异。这是以h:m:s格式返回的,因此我将其传递给TIME_TO_SEC()以转换为秒。我们现在可以除以300(5分钟内的秒数)并使用FLOOR()函数向下舍入到最接近的整数。这为我们提供了每种5分钟间隔的ID,范围从1到12,我们可以用GROUP BY。然后,我们会在SELECT中添加此ID,并为其添加别名window并添加WHERE&amp; AND条款确保我们仅包含1小时的数据,忽略最近的5分钟。

现在查看外部查询...我生成一个派生表windows,其中包含12行虚拟数据,只是整数1到12.我们可以LEFT OUTER JOIN内部查询也作为派生表ws,它允许我们确保总共返回12行。如果区间不包含任何数据,ws.sums将为null,但我们可以使用IFNULL()将其转换为0。

我不完全确定它会按照您的要求行事,所以如果您使用它,您应该确保进行必要的测试,特别是在间隔的边界上。