[英]How do I effectively select the average sum of several sums being calculated based on different timestamps in SQL?
我有一个数据库表,如下所示:
id | macaddr | load | timestamp
=========================================
1 | 0011111 | 17 | 2012-02-07 10:00:00
1 | 0011111 | 6 | 2012-02-07 12:00:00
2 | 0022222 | 3 | 2012-02-07 12:00:03
3 | 0033333 | 9 | 2012-02-07 12:00:04
4 | 0022222 | 4 | 2012-02-07 12:00:06
5 | 0033333 | 8 | 2012-02-07 12:00:10
...
现在,我想计算不同时间段(例如今天,昨天,本周,本月)所有设备(= mac地址)的平均负载。
通过首先找出不同时间点(样本日期)的总负荷总和,然后计算这些样本日期的负荷总和的平均值,可以计算平均负荷。 例如,如果我希望最近十秒钟的平均负载(现在是2012-02-07 12:00:10),则可以将采样日期确定为12:00:02、12:00: 04、12:00:06、12:00:08和12:00:10。 然后,我将通过汇总每个设备的最新负载值来计算负载总和:
2012-02-07 12:00:02 | 6 [= load(id=2)]
2012-02-07 12:00:04 | 18 [= load(id=2) + load(id=3) + load(id=4)]
2012-02-07 12:00:06 | 19 [= load(id=2) + load(id=4) + load(id=5)]
2012-02-07 12:00:08 | 19 [= load(id=2) + load(id=4) + load(id=5)]
2012-02-07 12:00:10 | 18 [= load(id=2) + load(id=5) + load(id=6)]
如果设备的负载值早于一个小时(此处为id = 1),则该负载值将被忽略。 在这种情况下,平均值为16。
当前,我使用许多“ UNION ALL”语句生成了一个相当复杂的查询,该语句非常慢:
SELECT avg(l.load_sum) as avg_load
FROM (
SELECT sum(so.load) AS load_sum
FROM (
SELECT *
FROM (
SELECT si.macaddr, si.load
FROM sensor_data si WHERE si.timestamp > '2012-02-07 11:00:10' AND si.timestamp < '2012-02-07 12:00:10'
ORDER BY si.timestamp DESC
) AS sm
GROUP BY macaddr
) so
UNION ALL
[THE SAME THING AGAIN WITH OTHER TIMESTAMPS]
UNION ALL
[AND AGAIN]
UNION ALL
[AND AGAIN]
...
) l
现在想象一下,我想计算一个月的平均负载。 对于每小时的采样日期,我需要使用UNION ALL语句加入30x24 = 720个查询。 整个查询需要将近一分钟才能在我的计算机上完成。 我相信没有UNION ALL语句会有更好的解决方案。 但是,我在网络上找不到任何有用的东西。 因此,非常感谢您的帮助!
使用Unix时间戳的一小部分:首先,我们计算每小时(3600秒)的平均值:
SELECT
macaddr,
sum(CAST(load AS float))/CAST(count(*) AS float) AS loadavg,
FLOOR(UNIX_TIMESTAMP(`timestamp`)/3600) AS hourbase
FROM sensor_data
GROUP BY macaddr,FLOOR(UNIX_TIMESTAMP(`timestamp`)/3600)
然后我们平均一个月
SELECT
avg(loadavg) as monthlyavg,
macaddr
FROM (
SELECT
macaddr,
sum(CAST(load AS float))/CAST(count(*) AS float) AS loadavg,
FLOOR(UNIX_TIMESTAMP(`timestamp`)/3600) AS hourbase
FROM sensor_data
WHERE `timestamp` BETWEEN '2012-01-07 12:00:00' AND '2012-02-07 11:59:59'
GROUP BY macaddr,FLOOR(UNIX_TIMESTAMP(`timestamp`)/3600)
) AS hourlies
GROUP BY macaddr, hourbase
为了使事情变得更容易,您应该创建一个“小时”函数,该函数返回一个日期时间,小时部分之后没有任何有效数字。 因此,现在(2012年2月2日下午5:05)将是2012-02-07 17:00。 这是您的小时函数的代码:
select dateadd(hh, DATEPART(hh, current_timestamp), DATEADD(dd, 0, datediff(dd, 0, current_timestamp)))
(将上述代码中的current_timestamp
替换为小时函数的datetime参数。我假设您将其创建为dbo.fnHour(),并且它带有datetime参数。
然后,您可以使用dbo.fnHour()作为分区函数来查询所需的内容。 您的sql看起来像这样:
select
avg(load) as avg_load
from (
select dbo.fnHour(si.timestamp) [hour], macaddr, sum(load) as [load]
from
sensor_data si
where
si.timestamp >= dateadd(mm, -1, current_timestamp)
group by
dbo.fnHour(si.timestamp), macaddr
) as f
我没有测试过,所以可能会有一些错别字,但这足以让您前进。
我可能会误解您想要做什么。 看起来您正在使事情变得比使用采样要复杂得多。 给出结果看起来应该是什么样的样本,也许可以使人们为您的特定案例提供更好的解决方案。
mysql> SELECT * FROM `test`;
+----+-----+------+------------+
| id | mac | load | when |
+----+-----+------+------------+
| 1 | 1 | 10 | 2012-02-01 |
| 2 | 1 | 20 | 2012-01-01 |
| 3 | 2 | 60 | 2011-09-01 |
+----+-----+------+------------+
mysql> SELECT avg(`sum_load`)
-> FROM
-> (
-> SELECT sum( `load` ) as sum_load
-> FROM `test`
-> WHERE `when` > '2011-01-15'
-> GROUP BY `mac`
-> ) as t1;
+-----------------+
| avg(`sum_load`) |
+-----------------+
| 45.0000 |
+-----------------+
mysql> SELECT avg(`sum_load`)
-> FROM
-> (
-> SELECT sum( `load` ) as sum_load
-> FROM `test`
-> WHERE `when` > '2011-01-15' AND `when` < '2012-01-15'
-> GROUP BY `mac`
-> ) as t1;
+-----------------+
| avg(`sum_load`) |
+-----------------+
| 40.0000 |
+-----------------+
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.