Pergunta

Eu tenho uma tabela no SQL Server que armazena estatísticas para uma peça de hardware, linhas na tabela representam os dados para um determinado segundo. Ele contém, por exemplo, estas colunas:

timestamp (DateTime)
value (int)

O que eu quero fazer é selecionar os dados da tabela para uma determinada data / intervalo de tempo, mas devolvê-lo de tal maneira a que as médias para um período de tempo determinado (tal como 1 minuto, 5 minutos, um dia etc) entre o intervalo dado. Então, por uma hora eu teria 60 linhas de médias 1 minuto.

Onde posso começar com isso? Qualquer pessoa quaisquer pontos ou idéias?

Foi útil?

Solução

Você pode fazer um seleto e GROUP BY em um PartData do seu timestamp.

Por exemplo:

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])

EDIT:. Para seus intervalos de tempo mais complexos como 5 minutos, você pode fazer uma divisão na datepart como segue

DATEPART(mi, [timestamp]) / 5 * 5

Outras dicas

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

Isso irá selecionar médias para todos os minutos dentro de uma determinada hora, mesmo aqueles para os quais não há registros.

Além da mensagem por Robin Day, você pode agrupar por intervalos de 5 minutos como:

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

E se você gostaria de abranger vários dias, o grupo de dy, para o dia do ano:

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

Se você estiver indo para ter uma alta taxa de leitura / gravação para esses dados você pode querer considerar uma exibição indexada. Eu tenho usado essa abordagem em todo o lugar para agregada por baldes de tempo. Eu só cheguei a blogging o exemplo , aqui está o código:

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

Você pode ver o exemplo de pesquisa no final do bloco de código. É claro que você pode definir intervalos para consulta em vez de apenas procurando um determinado par dayBucket / minuteBucket.

Eu não poderia obter a resposta de Quassnoi ao trabalho sem as seguintes alterações:

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top