1

我有一个如下所示的数据库表:

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 语句会有更好的解决方案。但是,我没有在网上找到任何有用的东西。因此,我将非常感谢您的帮助!

4

3 回答 3

1

使用 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-02-07T22:16:03.247 回答
0

我可能会误解您要做什么。看起来您正在使事情变得比使用采样所需的复杂得多。也许提供结果应该是什么样子的样本可以让人们为您的特定案例提供更好的解决方案。

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 |
+-----------------+
于 2012-02-07T22:12:40.627 回答
0

为了让你自己更容易,你应该创建一个“小时”函数,它返回一个在小时部分之后没有有效数字的日期时间。所以现在(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

我还没有测试过,所以可能会有一些错别字,但这应该足以让你继续前进。

于 2012-02-07T22:12:08.693 回答