7

我在 SQL Server 中有一个表,用于存储硬件的统计信息,表中的行表示给定秒的数据。例如,它包含以下列:

timestamp (DateTime)
value (int)

我想要做的是从表中选择给定日期/时间范围内的数据,但以给定时间段(例如 1 分钟、5 分钟、1 天等)在给定日期/时间范围内取平均值的方式返回它范围。所以一个小时我会有 60 行 1 分钟的平均值。

我从哪里开始?有人有任何观点或想法吗?

4

5 回答 5

9

您可以在时间戳的 DatePart 上进行选择和分组。

例如:

SELECT
    DATEPART(hh, [timestamp]),
    DATEPART(mi, [timestamp]),
    AVG([value])
FROM
    YourTable
WHERE
    [timestamp] BETWEEN '2009-01-01 00:00:00.000' AND '2009-02-01 00:00:00.000'
GROUP BY
    DATEPART(hh, [timestamp]),
    DATEPART(mi, [timestamp])

编辑:对于您更复杂的时间跨度(例如 5 分钟),您可以按如下方式对日期部分进行划分。

DATEPART(mi, [timestamp]) / 5 * 5
于 2009-06-16T11:44:21.017 回答
5
WITH    cal(m) AS
        (
        SELECT  1
        UNION ALL
        SELECT  m + 1
        FROM    cal
        WHERE   m < 60
        )
SELECT  DATEADD(minute, m, @start), AVG(value)
FROM    cal
LEFT JOIN
        timestamp
ON      timestamp > DATEADD(minute, m, @start)
        AND timestamp <= DATEADD(minute, m + 1, @start)
GROUP BY
        m

这将选择给定小时内所有分钟的平均值,即使是那些没有记录的分钟。

于 2009-06-16T11:48:22.367 回答
1

除了 Robin Day 的帖子之外,您还可以按 5 分钟的间隔分组,例如:

GROUP BY
    DATEPART(hh, [timestamp]),
    DATEPART(mi, [timestamp]) / 5

如果您想跨越几天,请在 dy 上分组,表示一年中的某一天:

GROUP BY
    DATEPART(dy, [timestamp]),
    DATEPART(hh, [timestamp]),
    DATEPART(mi, [timestamp]) / 5
于 2009-06-16T11:48:58.120 回答
1

如果您要对此数据具有较高的读/写比率,您可能需要考虑索引视图。我在各地都使用这种方法来按时间段进行汇总。我刚刚开始写博客示例,代码如下:

create table timeSeries (
    timeSeriesId int identity primary key clustered
    ,updateDate datetime not null
    ,payload float not null
)

insert timeSeries values ('2009-06-16 12:00:00', rand())
insert timeSeries values ('2009-06-16 12:00:59', rand())
insert timeSeries values ('2009-06-16 12:01:00', rand())
insert timeSeries values ('2009-06-16 12:59:00', rand())
insert timeSeries values ('2009-06-16 01:00:00', rand())
insert timeSeries values ('2009-06-16 1:30:00', rand())
insert timeSeries values ('2009-06-16 23:59:00', rand())
insert timeSeries values ('2009-06-17 00:01:00', rand())
insert timeSeries values ('2009-06-17 00:01:30', rand())


create view timeSeriesByMinute_IV with schemabinding as
select
    dayBucket = datediff(day, 0, updateDate)
    ,minuteBucket = datediff(minute, 0, (updateDate - datediff(day, 0, updateDate)))
    ,payloadSum = sum(payLoad)
    ,numRows = count_big(*) 
from dbo.timeSeries
group by 
    datediff(day, 0, updateDate)
    ,datediff(minute, 0, (updateDate - datediff(day, 0, updateDate)))
go

create unique clustered index CU_timeSeriesByMinute_IV on timeSeriesByMinute_IV (dayBucket, minuteBucket)
go


create view timeSeriesByMinute as
select
    dayBucket
    ,minuteBucket
    ,payloadSum
    ,numRows
    ,payloadAvg = payloadSum / numRows
from dbo.timeSeriesByMinute_IV with (noexpand)
go

declare @timeLookup datetime, @dayBucket int, @minuteBucket int
select 
    @timeLookup = '2009-06-16 12:00:00'
    ,@dayBucket = datediff(day, 0, @timeLookup)
    ,@minuteBucket = datediff(minute, 0, (@timeLookup - datediff(day, 0, @timeLookup)))

select * from timeSeriesByMinute where dayBucket = @dayBucket and minuteBucket = @minuteBucket

您可以在代码块的末尾看到示例查找。显然,您可以定义要查询的范围,而不仅仅是寻找特定的 dayBucket/minuteBucket 对。

于 2009-06-16T13:40:11.627 回答
1

如果没有以下更改,我无法得到 Quassnoi 的回答:

WITH    cal(m) AS
    (
    SELECT  1
    UNION ALL
    SELECT  m + 1
    FROM    cal
    WHERE   m < 60
    )
SELECT  DATEADD(minute, m, @start) m, AVG(value)
FROM    cal
LEFT JOIN
    YourTable
ON      timestamp > DATEADD(minute, m, @start)
    AND timestamp <= DATEADD(minute, m + 1, @start)
GROUP BY
    m
于 2009-11-07T16:37:44.603 回答