2009-06-16 16 views
7

Ho una tabella in SQL Server che memorizza le statistiche per un pezzo di hardware, le righe nella tabella rappresentano i dati per un dato secondo. Esso contiene, ad esempio, queste colonne:Tempo medio T-SQL

timestamp (DateTime) 
value (int) 

Quello che voglio fare è selezionare i dati dalla tabella la data e determinato intervallo/tempo, ma restituirlo in modo tale che le medie per un periodo determinato di tempo (ad esempio 1 minuto, 5 minuti, 1 giorno ecc.) tra l'intervallo specificato. Quindi per un'ora avrei avuto 60 righe di medie da 1 minuto.

Da dove iniziare? Qualcuno ha punti o idee?

risposta

9

È possibile selezionare e raggruppare in base a un DatePart del timestamp.

Per esempio:

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: Per il vostro tempo in modo più complesso si estende come 5 minuti, si può fare una divisione sulla datepart come segue.

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

+1 bello di query e di facile lettura – Andomar

+0

Questo funziona a meraviglia, grazie! – 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 

Questo selezionerà le medie per tutti i minuti all'interno di una determinata ora, anche quelli per i quali non ci sono record.

+0

+1. Ben fatto. ... –

+0

Costruire una tabella numerica con un CTE ricorsivo, non visto prima - mi piace! – AakashM

+0

+1 per il CTE! –

1

Oltre al post di Robin Day, è possibile raggruppare da intervalli di 5 minuti come:

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

E se vi piacerebbe protraggono per diversi giorni, gruppo su dy, per il giorno dell'anno:

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

Se si dispone di un elevato rapporto lettura/scrittura per questi dati, è possibile prendere in considerazione una vista indicizzata. Ho usato questo approccio dappertutto per aggregare in base al tempo. Ho appena ricevuto intorno a blogging the example, ecco il codice:

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 

è possibile vedere l'esempio di ricerca alla fine del blocco di codice. È chiaro che è possibile definire intervalli per eseguire query su invece di cercare una determinata coppia dayBucket/minuteBucket.

1

non ho potuto ottenere la risposta di Quassnoi a lavorare senza le seguenti modifiche:

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