2009-06-16 86 views
7

Tengo una tabla en SQL Server que almacena estadísticas para un hardware, las filas en la tabla representan datos para un segundo dado. Que contiene, por ejemplo, estas columnas:Promedio de tiempo T-SQL

timestamp (DateTime) 
value (int) 

Lo que quiero hacer es seleccionar los datos de la tabla para un rango de fecha/hora determinada, pero volver de una manera tal que el promedio es de un período de tiempo determinado (tal como 1 minuto, 5 minutos, 1 día, etc.) entre el rango dado. Entonces, durante una hora tendría 60 filas de promedios de 1 minuto.

¿Por dónde empiezo con esto? ¿Alguien tiene puntos o ideas?

Respuesta

9

Puede hacer una selección y agrupar en un DatePart de su marca de tiempo.

Por ejemplo:

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 su tiempo más complejo se extiende como 5 minutos, se puede hacer una división en la datepart de la siguiente manera.

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

+1 consulta agradable y fácil de leer – Andomar

+0

Esto funciona un regalo, gracias! – Lloyd

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 

Esto seleccionará promedios para todos los minutos dentro de una hora dada, incluso aquellos para los cuales no existen registros.

+0

+1. Buena esa. ... –

+0

Construyendo una tabla numérica con un CTE recursivo, no visto antes - ¡me gusta! – AakashM

+0

+1 para el CTE! –

1

Además del mensaje por Robin Day, puede agrupar por intervalos de 5 minutos como:

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

Y si le gustaría abarcar varios días, el grupo de dy, para el día del año:

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

Si va a tener una alta relación de lectura/escritura para estos datos, es posible que desee considerar una vista indizada. He utilizado este enfoque en todo el lugar para agregar por períodos de tiempo. Acabo de recibir en torno a blogging the example, aquí está el 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 

se puede ver el ejemplo de búsqueda al final del bloque de código. Claramente puede definir los rangos para consultar a través de, en lugar de solo buscar un par particular dayBucket/minuteBucket.

1

no pude conseguir la respuesta de Quassnoi a trabajar sin los siguientes cambios:

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