Question

J'ai une table dans SQL Server qui stocke les statistiques pour un élément matériel, les lignes de la table représentent les données pour une seconde donnée. Il contient par exemple ces colonnes:

timestamp (DateTime)
value (int)

Ce que je veux faire, c'est sélectionner les données du tableau pour une plage de dates / heures donnée, mais les renvoyer de manière à ce qu'elles soient moyennes pour une période donnée (telle que 1 minute, 5 minutes, 1 jour, etc.) entre la plage donnée. Donc, pendant une heure, j'aurais 60 rangées de moyennes sur 1 minute.

Par quoi dois-je commencer? Quelqu'un a des points ou des idées?

Était-ce utile?

La solution

Vous pouvez effectuer une sélection et un regroupement sur une DatePart de votre horodatage.

Par exemple:

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: pour les plages de temps plus complexes telles que 5 minutes, vous pouvez diviser la date du jour de la manière suivante:

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

Autres conseils

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

Ceci sélectionnera les moyennes pour toutes les minutes d'une heure donnée, même celles pour lesquelles il n'y a pas d'enregistrements.

En plus du message de Robin Day, vous pouvez regrouper par intervalles de 5 minutes, par exemple:

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

Et si vous souhaitez couvrir plusieurs jours, groupe le jour de l’année:

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

Si vous envisagez un rapport lecture / écriture élevé pour ces données, vous pouvez envisager une vue indexée. J'ai utilisé cette approche un peu partout pour regrouper des tranches de temps. Je viens de me rendre à bloguant l'exemple , voici le code:

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

Vous pouvez voir l'exemple de recherche à la fin du bloc de code. Clairement, vous pouvez définir des plages de requête au lieu de rechercher uniquement une paire dayBucket / minuteBucket particulière.

Je ne pouvais pas obtenir la réponse de Quassnoi sans les modifications suivantes:

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top