Усреднение времени T-SQL - PullRequest
7 голосов
/ 16 июня 2009

У меня есть таблица в SQL Server, в которой хранится статистика для единицы оборудования, строки в таблице представляют данные за данную секунду. Например, он содержит следующие столбцы:

timestamp (DateTime)
value (int)

Что я хочу сделать, это выбрать данные из таблицы для заданного диапазона дат / времени, но вернуть их таким образом, чтобы они усреднялись за определенный период времени (например, 1 минута, 5 минут, 1 день и т. Д.) между заданным диапазоном. Так что в течение часа у меня будет 60 рядов по 1 минуте в среднем.

С чего мне начать? У кого-нибудь есть вопросы или идеи?

Ответы [ 5 ]

9 голосов
/ 16 июня 2009

Вы можете выбрать и сгруппировать по 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
5 голосов
/ 16 июня 2009
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

Это выберет средние значения для всех минут в течение данного часа, даже для тех, для которых нет записей.

1 голос
/ 07 ноября 2009

Я не смог заставить работать ответ Кассной без следующих изменений:

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
1 голос
/ 16 июня 2009

Если вы хотите иметь высокий коэффициент чтения / записи для этих данных, вы можете рассмотреть индексированное представление. Я использовал этот подход повсеместно для агрегирования по времени. Я только что нашел блогов на примере , вот код:

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.

1 голос
/ 16 июня 2009

В дополнение к сообщению Робина Дея вы можете группировать по 5-минутным интервалам, например:

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

А если вы хотите охватить несколько дней, группа по дню для дня года:

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