Comment grouper le temps par heure ou par 10 minutes

167

comme quand je fais

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date]

comment puis-je spécifier la période du groupe?

MS SQL 2008

2e édition

j'essaie

SELECT MIN([Date]) AS RecT, AVG(Value)
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY (DATEPART(MINUTE, [Date]) / 10)
  ORDER BY RecT

changé% 10 en / 10. Est-il possible de faire une sortie de date sans millisecondes?

cnd
la source

Réponses:

213

enfin fini avec

GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)
cnd
la source
11
Je l'ai fait ROUND((DATEPART(MINUTE, DT.[Date]) / 5),0,1) * 5, de sorte que lorsque je regarde les données, elles sont corrélées avec le créneau horaire le plus proche
hdost
7
L'année, le mois et le jour peuvent être simplifiés en DATE(DT.[Date]).
@Keelan Ne fonctionne pas pour moi - mais CONVERT (date, DT. [Date]) le fait.
Dan Parsonson
datepart(hour, workingPolicy.workingHours)/2.0donne 1.5tandis que datepart(hour, '1900-01-01 09:00:30.000')/2.0donne 4.5, je ne comprends pas pourquoi? Remarque: workingPolicy.workingHours = 1900-01-01 09: 00: 30.000 . s'il vous plaît aider
affanBajwa
65

Je suis très en retard à la fête, mais cela n'apparaît dans aucune des réponses existantes:

GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', date_column) / 10 * 10, '2000')
  • Les termes 10et MINUTEpeuvent être modifiés en n'importe quel nombre et DATEPART, respectivement.
  • C'est une DATETIMEvaleur, ce qui signifie:
    • Cela fonctionne bien sur de longs intervalles de temps. (Il n'y a pas de collision entre les années.)
    • L'inclure dans l' SELECTinstruction donnera à votre sortie une colonne avec une jolie sortie tronquée au niveau que vous spécifiez.
  • '2000'est une "date d'ancrage" autour de laquelle SQL effectuera le calcul de la date. Jereonh a découvert ci - dessous que vous rencontrez un débordement d'entier avec l'ancre précédente ( 0) lorsque vous regroupez les dates récentes par secondes ou millisecondes.
SELECT   DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
                                                               AS [date_truncated],
         COUNT(*) AS [records_in_interval],
         AVG(aa.[value]) AS [average_value]
FROM     [friib].[dbo].[archive_analog] AS aa
GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
ORDER BY [date_truncated]

Si vos données couvrent des siècles, ‡ l' utilisation d'une seule date d'ancrage pour le regroupement en seconde ou en milliseconde rencontrera toujours le débordement. Si cela se produit, vous pouvez demander à chaque ligne d'ancrer la comparaison de regroupement à minuit de sa propre date:

  • Utilisez DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0)plutôt que '2000'partout où il apparaît ci-dessus. Votre requête sera totalement illisible, mais cela fonctionnera.

  • Une alternative pourrait être CONVERT(DATETIME, CONVERT(DATE, aa.[date]))le remplacement.

2 32 ≈ 4.29E + 9, donc si votre DATEPARTest - SECOND, vous obtenez 4,3 milliards de secondes de chaque côté, ou « ancre ± 136 ans. » De même, 2 32 millisecondes équivaut à ≈ 49,7 jours.
Si vos données couvrent réellement des siècles ou des millénaires et sont toujours exactes à la seconde ou à la milliseconde… félicitations! Quoi que vous fassiez, continuez à le faire.

Michael - Où est Clay Shirky
la source
serait-il possible de le faire à partir d'une date de début spécifique?
Pylander
1
@pylander Bien sûr. Mettez simplement une whereclause avant le group by.
Michael - Où est Clay Shirky
2
Je vois. Est-ce que changer cela équivaudrait à / 20 * 20collecter des données par intervalles de 20 minutes? Désolé, j'ai du mal à faire des maths en ce moment
déséquilibré à
2
pour SECOND comme DATEPART j'obtiens un message d'erreur ( The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.). Il semble que MINUTE soit la plus petite partie de date que vous puissiez utiliser avec cette approche.
jeroenh
1
@jeroenh, vous avez raison. J'ai ajouté une section pour en parler. tldr: remplacez le 0par '2000'(les guillemets sont importants!) et réessayez SECOND.
Michael - Où est Clay Shirky
17

Dans T-SQL, vous pouvez:

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date], DATEPART(hh, [Date])

ou

à la minute DATEPART(mi, [Date])

ou

par 10 minutes d'utilisation DATEPART(mi, [Date]) / 10(comme Timothy l'a suggéré)

tzup
la source
1
GROUP BY [Date], DATEPART (hh, [Date]) est un gâchis, non?
cnd le
1
@nCdy Ça devrait aller, sinon vous obtenez une erreur "... invalide dans la liste de sélection car elle n'est contenue ni dans une fonction d'agrégation ni dans la clause GROUP BY"
tzup
1
Comment regroupez-vous par date entière et par heures dans le même temps? J'utilise juste la date min.
cnd
si vous utilisez Min (Date), vous pouvez bien sûr retirer la Date dans le Group By.
tzup
3
Les minutes divisées par 10 créeront un regroupement par intervalles de 10 minutes, ce qui est nécessaire. Modulo 10 n'a aucun sens.
Tar
12

Pour un intervalle de 10 minutes, vous

GROUP BY (DATEPART(MINUTE, [Date]) / 10)

Comme cela a déjà été mentionné par tzup et Pieter888 ... pour faire un intervalle d'une heure, juste

GROUP BY DATEPART(HOUR, [Date])
Timothy Khouri
la source
2
mais ici je groupe les minutes de 1980 avec les minutes de 2011: SI doit s'en soucier.
cnd le
Est-ce que% serait le calcul correct? Cela ne créerait-il pas 10 groupes. Par exemple: 1% 10 = 9 2% 10 = 8 Ce ne serait même pas nécessairement le bon groupement chronologique non plus. Je pense qu'une simple division normale serait correcte. Sauf si% n'est pas une division de reste en sql.
Steven
12
Les minutes divisées par 10 créeront un regroupement par intervalles de 10 minutes, ce qui est nécessaire. Modulo 10 n'a aucun sens.
Tar
D'accord avec Tar. Le regroupement n'a pas de sens.
MikeMurko
7

Devrait être quelque chose comme

select timeslot, count(*)  
from 
    (
    select datepart('hh', date) timeslot
    FROM [FRIIB].[dbo].[ArchiveAnalog]  
    ) 
group by timeslot

(Je ne suis pas sûr à 100% de la syntaxe - je suis plutôt du genre Oracle)

Dans Oracle:

SELECT timeslot, COUNT(*) 
FROM
(  
    SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot 
    FROM
    (
        SELECT l_time FROM mytab  
    )  
) GROUP BY timeslot 
Frank Schmitt
la source
6

La réponse originale donnée par l'auteur fonctionne plutôt bien. Juste pour étendre cette idée, vous pouvez faire quelque chose comme

group by datediff(minute, 0, [Date])/10

ce qui vous permettra de regrouper par une période plus longue que 60 minutes, disons 720, soit une demi-journée etc.

Derek
la source
1
Quelle serait la requête MySQL pour ce qui précède?
Biranchi le
4

Pour MySql:

GROUP BY
DATE(`your_date_field`),
HOUR(`your_date_field`),
FLOOR( MINUTE(`your_date_field`) / 10);
nobiliste
la source
1

Ma solution consiste à utiliser une fonction pour créer une table avec les intervalles de dates, puis à joindre cette table aux données que je souhaite regrouper en utilisant l'intervalle de dates dans la table. L'intervalle de date peut alors être facilement sélectionné lors de la présentation des données.

CREATE FUNCTION [dbo].[fn_MinuteIntervals]
    (
      @startDate SMALLDATETIME ,
      @endDate SMALLDATETIME ,
      @interval INT = 1
    )
RETURNS @returnDates TABLE
    (
      [date] SMALLDATETIME PRIMARY KEY NOT NULL
    )
AS
    BEGIN
        DECLARE @counter SMALLDATETIME
        SET @counter = @startDate
        WHILE @counter <= @endDate
            BEGIN
                INSERT INTO @returnDates VALUES ( @counter )
                SET @counter = DATEADD(n, @interval, @counter)
            END
        RETURN
    END
user3193141
la source
1
declare @interval tinyint
set @interval = 30
select dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0), sum(Value_Transaction)
from Transactions
group by dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0)
Rodas PT
la source
8
Bienvenue dans stackoverflow. Vous devez expliquer en détail votre réponse et ce que cela ajoute aux 10 autres réponses déjà présentes sur le site.
Simon.SA
0

Pour SQL Server 2012, même si je pense que cela fonctionnerait dans SQL Server 2008R2, j'utilise l'approche suivante pour réduire le temps à la milliseconde:

DATEADD(MILLISECOND, -DATEDIFF(MILLISECOND, CAST(time AS DATE), time) % @msPerSlice, time)

Cela fonctionne par:

  • Obtenir le nombre de millisecondes entre un point fixe et une heure cible:
    @ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
  • En prenant le reste de la division de ces millisecondes en tranches de temps:
    @rms = @ms % @msPerSlice
  • Ajout du négatif de ce reste au temps cible pour obtenir le temps de tranche:
    DATEADD(MILLISECOND, -@rms, time)

Malheureusement, tout comme cela déborde de microsecondes et d'unités plus petites, des ensembles de données plus grands et plus fins devraient utiliser un point fixe moins pratique.

Je n'ai pas rigoureusement évalué cela et je ne suis pas dans le Big Data, donc votre kilométrage peut varier, mais les performances n'étaient pas sensiblement pires que les autres méthodes essayées sur nos équipements et ensembles de données, et le paiement en commodité pour les développeurs pour le découpage arbitraire en vaut la peine. pour nous.

tychon
la source
0
select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
from [FRIIB].[dbo].[ArchiveAnalog]
group by RecT
order by RecT;

remplacez les deux 600 par n'importe quel nombre de secondes que vous souhaitez grouper.

Si vous en avez souvent besoin et que la table ne change pas, comme le suggère le nom Archive, il serait probablement un peu plus rapide de convertir et de stocker la date (et l'heure) sous forme d'heure unix dans la table.

theking2
la source
0

Je sais que je suis en retard au spectacle avec celui-ci, mais j'ai utilisé cette approche assez simple. Cela vous permet d'obtenir les tranches de 60 minutes sans aucun problème d'arrondi.

Select 
   CONCAT( 
            Format(endtime,'yyyy-MM-dd_HH:'),  
            LEFT(Format(endtime,'mm'),1),
            '0' 
          ) as [Time-Slice]
Jessé
la source
0
select dateadd(minute, datediff(minute, 0, Date), 0),
       sum(SnapShotValue)
FROM [FRIIB].[dbo].[ArchiveAnalog]
group by dateadd(minute, datediff(minute, 0, Date), 0)
Jakub Dvorak
la source
1
Veuillez fournir quelques explications lorsque vous répondez à une question.
Shizzen83 le