web-dev-qa-db-fra.com

Regrouper la date et l'heure en intervalles de 5, 15, 30 et 60 minutes

J'essaie de regrouper certains enregistrements en intervalles de 5, 15, 30 et 60 minutes:

SELECT AVG(value) as "AvgValue",
sample_date/(5*60) as "TimeFive"
FROM DATA
WHERE id = 123 AND sample_date >= 3/21/2012

je veux exécuter plusieurs requêtes, chacune regrouperait mes valeurs moyennes dans les incréments de temps souhaités. Ainsi, la requête de 5 minutes retournerait des résultats comme celui-ci:

AvgValue  TimeFive
6.90      1995-01-01 00:05:00
7.15      1995-01-01 00:10:00
8.25      1995-01-01 00:15:00

La requête de 30 minutes entraînerait ceci:

AvgValue  TimeThirty 
6.95      1995-01-01 00:30:00
7.40      1995-01-01 01:00:00

La colonne datetime est dans yyyy-mm-dd hh:mm:ss format

Je reçois des erreurs de conversion implicites de ma colonne datetime. Toute aide est très appréciée!

15
jrubengb

En utilisant

datediff(minute, '1990-01-01T00:00:00', yourDatetime)

vous donnera le nombre de minutes depuis le 1990-1-1 (vous pouvez utiliser la date de base souhaitée).

Ensuite, vous pouvez diviser par 5, 15, 30 ou 60 et regrouper par le résultat de cette division. J'ai vérifié qu'il sera évalué comme une division entière, vous obtiendrez donc un nombre entier que vous pouvez utiliser pour vous regrouper.

c'est à dire.

group by datediff(minute, '1990-01-01T00:00:00', yourDatetime) /5

UPDATE Comme la question d'origine a été modifiée pour exiger que les données soient affichées au format date-heure après le regroupement, j'ai ajouté cette requête simple qui fera l'affaire ce que le PO veut:

-- This convert the period to date-time format
SELECT 
    -- note the 5, the "minute", and the starting point to convert the 
    -- period back to original time
    DATEADD(minute, AP.FiveMinutesPeriod * 5, '2010-01-01T00:00:00') AS Period,
    AP.AvgValue
FROM
    -- this groups by the period and gets the average
    (SELECT
        P.FiveMinutesPeriod,
        AVG(P.Value) AS AvgValue
    FROM
        -- This calculates the period (five minutes in this instance)
        (SELECT 
            -- note the division by 5 and the "minute" to build the 5 minute periods
            -- the '2010-01-01T00:00:00' is the starting point for the periods
            datediff(minute, '2010-01-01T00:00:00', T.Time)/5 AS FiveMinutesPeriod,
            T.Value
        FROM Test T) AS P
    GROUP BY P.FiveMinutesPeriod) AP

REMARQUE: J'ai divisé cela en 3 sous-requêtes pour plus de clarté. Vous devriez le lire de l'intérieur. Il pourrait, bien sûr, être écrit comme une seule requête compacte

REMARQUE: si vous modifiez la période et la date-heure de début, vous pouvez obtenir tout intervalle dont vous avez besoin, comme des semaines à partir d'un jour donné, ou tout ce dont vous avez besoin

Si vous souhaitez générer des données de test pour cette requête, utilisez ceci:

CREATE TABLE Test
( Id INT IDENTITY PRIMARY KEY,
Time DATETIME,
Value FLOAT)

INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:00:22', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:03:22', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:04:45', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:07:21', 20)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:10:25', 30)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:11:22', 30)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:14:47', 30)

Le résultat de l'exécution de la requête est le suivant:

Period                     AvgValue
2012-03-22 00:00:00.000    10
2012-03-22 00:05:00.000    20
2012-03-22 00:10:00.000    30
18
JotaBe

En s'appuyant sur la réponse de @ JotaBe (sur laquelle je ne peux pas commenter - sinon je le ferais), vous pouvez également essayer quelque chose comme ça qui ne nécessiterait pas de sous-requête.

 SELECT 
    AVG(value) AS 'AvgValue',

    -- Add the rounded seconds back onto Epoch to get rounded time
    DATEADD(
        MINUTE,
        (DATEDIFF(MINUTE, '1990-01-01T00:00:00', your_date) / 30) * 30,
        '1990-01-01T00:00:00'
    )      AS 'TimeThirty'

 FROM YourTable
 -- WHERE your_date > some max lookback period
 GROUP BY
    (DATEDIFF(MINUTE, '1990-01-01T00:00:00', your_date) / 30)

Cette modification supprime les tables temporaires et les sous-requêtes. Il utilise la même logique de base pour le regroupement par intervalles de 30 minutes mais, lorsque je présente les données en retour dans le résultat, j'inverse simplement le calcul de l'intervalle pour obtenir la date et l'heure arrondies.

8
DJ Sipe

Donc, au cas où vous googlé cela, mais vous devez le faire dans mysql, ce qui était mon cas:

Dans MySQL, vous pouvez faire

GROUP BY
CONCAT(
    DATE_FORMAT(`timestamp`,'%m-%d-%Y %H:'),
    FLOOR(DATE_FORMAT(`timestamp`,'%i')/5)*5
)
7
chiliNUT