Gruppierung in Intervallen von 5 Minuten innerhalb eines Zeitbereichs

88

Ich habe einige Schwierigkeiten mit mySQL-Befehlen, die ich ausführen möchte.

SELECT a.timestamp, name, count(b.name) 
FROM time a, id b 
WHERE a.user = b.user
  AND a.id = b.id
  AND b.name = 'John'
  AND a.timestamp BETWEEN '2010-11-16 10:30:00' AND '2010-11-16 11:00:00' 
GROUP BY a.timestamp

Dies ist meine aktuelle Ausgabeanweisung.

timestamp            name  count(b.name)
-------------------  ----  -------------
2010-11-16 10:32:22  John  2
2010-11-16 10:35:12  John  7
2010-11-16 10:36:34  John  1
2010-11-16 10:37:45  John  2
2010-11-16 10:48:26  John  8
2010-11-16 10:55:00  John  9
2010-11-16 10:58:08  John  2

Wie gruppiere ich sie in 5-Minuten-Intervallergebnisse?

Ich möchte, dass meine Ausgabe so ist

timestamp            name  count(b.name)
-------------------  ----  -------------
2010-11-16 10:30:00  John  2
2010-11-16 10:35:00  John  10
2010-11-16 10:40:00  John  0
2010-11-16 10:45:00  John  8
2010-11-16 10:50:00  John  0
2010-11-16 10:55:00  John  11 
Himmel
quelle

Antworten:

144

Dies funktioniert mit jedem Intervall.

PostgreSQL

SELECT
    TIMESTAMP WITH TIME ZONE 'epoch' +
    INTERVAL '1 second' * round(extract('epoch' from timestamp) / 300) * 300 as timestamp,
    name,
    count(b.name)
FROM time a, id 
WHERE 
GROUP BY 
round(extract('epoch' from timestamp) / 300), name


MySQL

SELECT
    timestamp,  -- not sure about that
    name,
    count(b.name)
FROM time a, id 
WHERE 
GROUP BY 
UNIX_TIMESTAMP(timestamp) DIV 300, name
Boecko
quelle
oh ... habe die MySQL-Flagge nicht bekommen ... es ist eine Postgresql-Abfrage ... aber im Grunde sollte dies auch mit
MySQL
2
ok .. statt zu extrahieren .. GROUP BY Runde (UNIX_TIMESTAMP (Zeitstempel) / 300) sollte den Trick machen
Boecko
2
@ pHiLs Kommentar ist auf mySql korrekt. Sie sollten DIV anstelle von round (/) verwenden, da sonst die Grenze zwischen den Intervallen falsch ist
DavidC
1
Ich habe es gerade mit mehreren Datensätzen versucht und die zweite Abfrage funktioniert hervorragend für MySQL, was das Anliegen der OP war. Können wir, da @sky abwesend zu sein scheint, einen Gruppenkonsens darüber erzielen, ist dies die Antwort?
Joey T
1
Ich habe das auch versucht. Die erste Aufzeichnung wird jedes Mal falsch angezeigt, wenn ein Intervall von 2 Minuten oder 3 Minuten und weitere Intervalle von 5 Minuten angezeigt werden. Hinweis: - Ich habe eine Bedingung hinzugefügt, um die letzten 15 Minuten Datensätze zu erhalten.
Ritesh
31

Ich bin auf das gleiche Problem gestoßen.

Ich fand heraus, dass es einfach ist, nach Minutenintervallen zu gruppieren, indem man die Epoche in Minuten in Minuten teilt und dann entweder rundet oder den Boden verwendet, um den Rest zu fahren. Wenn Sie also ein Intervall von 5 Minuten erhalten möchten, würden Sie 300 Sekunden verwenden .

    SELECT COUNT(*) cnt, 
    to_timestamp(floor((extract('epoch' from timestamp_column) / 300 )) * 300) 
    AT TIME ZONE 'UTC' as interval_alias
    FROM TABLE_NAME GROUP BY interval_alias
interval_alias       cnt
-------------------  ----  
2010-11-16 10:30:00  2
2010-11-16 10:35:00  10
2010-11-16 10:45:00  8
2010-11-16 10:55:00  11 

Dadurch werden die Daten korrekt nach dem ausgewählten Minutenintervall gruppiert zurückgegeben. Es werden jedoch keine Intervalle zurückgegeben, die keine Daten enthalten. Um diese leeren Intervalle zu erhalten, können wir die Funktion generate_series verwenden .

    SELECT generate_series(MIN(date_trunc('hour',timestamp_column)),
    max(date_trunc('minute',timestamp_column)),'5m') as interval_alias FROM 
    TABLE_NAME

Ergebnis:

interval_alias       
-------------------    
2010-11-16 10:30:00  
2010-11-16 10:35:00
2010-11-16 10:40:00   
2010-11-16 10:45:00
2010-11-16 10:50:00   
2010-11-16 10:55:00   

Um nun das Ergebnis mit einem Intervall von null Vorkommen zu erhalten, verbinden wir beide Ergebnismengen nur von außen .

    SELECT series.minute as interval,  coalesce(cnt.amnt,0) as count from 
       (
       SELECT count(*) amnt,
       to_timestamp(floor((extract('epoch' from timestamp_column) / 300 )) * 300)
       AT TIME ZONE 'UTC' as interval_alias
       from TABLE_NAME  group by interval_alias
       ) cnt
    
    RIGHT JOIN 
       (    
       SELECT generate_series(min(date_trunc('hour',timestamp_column)),
       max(date_trunc('minute',timestamp_column)),'5m') as minute from TABLE_NAME 
       ) series
  on series.minute = cnt.interval_alias

Das Endergebnis enthält die Serie mit allen 5-Minuten-Intervallen, auch ohne Intervalle.

interval             count
-------------------  ----  
2010-11-16 10:30:00  2
2010-11-16 10:35:00  10
2010-11-16 10:40:00  0
2010-11-16 10:45:00  8
2010-11-16 10:50:00  0 
2010-11-16 10:55:00  11 

Das Intervall kann leicht geändert werden, indem der letzte Parameter von generate_series angepasst wird. In unserem Fall verwenden wir '5m', aber es kann jedes gewünschte Intervall sein .

Nestor Martinez
quelle
1
Es wäre gewesen, wenn es MySQL gewesen wäre. Generate_series scheint eine PostgreSQL-Funktion zu sein. Schade.
Andreas
Die erste Abfrage, die nur das Ergebnis der aktuellen Daten liefert, zählt mittlere Datensätze von 2 Zeiträumen in beiden Zeiträumen. Wie in zwei Zeiträumen, 10:35 und 10:40, zählt es in beiden Gruppen 10:40, dh eins in 10:35 bis 10:40 und 10:40 bis 10:45.
Prem Popatia
29

Sie sollten lieber GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300anstelle von round (../ 300) verwenden, da ich festgestellt habe, dass einige Datensätze in zwei gruppierte Ergebnismengen gezählt werden.

pHiL
quelle
Dies ist richtig, die Runde (../ 300) hat es auf mySql
DavidC am
1
Für diejenigen, die neugierig sind, ist DIVMySQL floor()eine Float-Abteilung, die mit BIGINTs sicher ist .
Eric L.
1
Ich habe das auch versucht. Die erste Aufzeichnung wird jedes Mal falsch angezeigt, wenn ein Intervall von 2 Minuten oder 3 Minuten und weitere Intervalle von 5 Minuten angezeigt werden. Hinweis: - Ich habe eine Bedingung hinzugefügt, um die letzten 15 Minuten Datensätze zu erhalten.
Ritesh
Man sollte TRUNCATE oder FLOOR anstelle von ROUND verwenden, da das Rundungsverhalten nicht genau definiert ist und von der verwendeten C-Bibliothek abhängt. listen.mysql.com/mysql/93613
MrLeeh
27

Für Postgres fand ich es einfacher und genauer, die zu verwenden

date_trunc

Funktion, wie:

select name, sum(count), date_trunc('minute',timestamp) as timestamp
FROM table
WHERE xxx
GROUP BY name,date_trunc('minute',timestamp)
ORDER BY timestamp

Sie können verschiedene Auflösungen wie 'Minute', 'Stunde', 'Tag' usw. für date_trunc angeben.

Scott Persinger
quelle
7
@tmarthal - es sollte nicht positiv bewertet werden. Die ursprüngliche Frage war für MySQL.
Buggedcom
30
Wo stellen Sie das 5hier für das 5-Minuten-Intervall ein?
Älterer Gott
Ändern Sie aus diesem Grund die WHERE-Klausel in: WHERE-Zeitstempel> current_timestamp - Intervall '5 Minuten'
Luke Smith
2
Diese Abfrage scheint nicht das zu tun, was gestellt wird. Die Frage lautet "alle 5 Minuten", nicht 5 Minuten zuvor. Antwort passend, um herabgestimmt zu werden
Mohammed Rafeeq
11

Die Abfrage lautet wie folgt:

SELECT 
  DATE_FORMAT(
    MIN(timestamp),
    '%d/%m/%Y %H:%i:00'
  ) AS tmstamp,
  name,
  COUNT(id) AS cnt 
FROM
  table
GROUP BY ROUND(UNIX_TIMESTAMP(timestamp) / 300), name
WASD42
quelle
4

Sie müssen wahrscheinlich Ihren Zeitstempel in ymd: HM aufteilen und DIV 5 verwenden, um die Minuten in 5-Minuten-Bins aufzuteilen - so etwas wie

select year(a.timestamp), 
       month(a.timestamp), 
       hour(a.timestamp), 
       minute(a.timestamp) DIV 5,
       name, 
       count(b.name)
FROM time a, id b
WHERE a.user = b.user AND a.id = b.id AND b.name = 'John' 
      AND a.timestamp BETWEEN '2010-11-16 10:30:00' AND '2010-11-16 11:00:00'
GROUP BY year(a.timestamp), 
       month(a.timestamp), 
       hour(a.timestamp), 
       minute(a.timestamp) DIV 12

... und futz dann die Ausgabe im Client-Code so, dass sie so aussieht, wie Sie es möchten. Sie können auch die gesamte Datumszeichenfolge mit dem SQL Concat-Operator erstellen, anstatt separate Spalten abzurufen, wenn Sie möchten.

select concat(year(a.timestamp), "-", month(a.timestamp), "-" ,day(a.timestamp), 
       " " , lpad(hour(a.timestamp),2,'0'), ":", 
       lpad((minute(a.timestamp) DIV 5) * 5, 2, '0'))

... und dann gruppieren

Bill Dueber
quelle
Hmmm ... Aber die Ausgabe bekommt nicht das, was ich versuche zu bekommen. Es gibt eine Spalte zurück und ich bin nicht sehr sicher, was der Wert der Zählung ist ...
Himmel
2

Ich bin mir nicht sicher, ob du es noch brauchst.

SELECT FROM_UNIXTIME(FLOOR((UNIX_TIMESTAMP(timestamp))/300)*300) AS t,timestamp,count(1) as c from users GROUP BY t ORDER BY t;

2016-10-29 19:35:00 | 2016-10-29 19:35:50 | 4 |

2016-10-29 19:40:00 | 2016-10-29 19:40:37 | 5 |

2016-10-29 19:45:00 | 2016-10-29 19:45:09 | 6 |

2016-10-29 19:50:00 | 2016-10-29 19:51:14 | 4 |

2016-10-29 19:55:00 | 2016-10-29 19:56:17 | 1 |

user7088930
quelle
1

Wie wäre es mit diesem:

select 
    from_unixtime(unix_timestamp(timestamp) - unix_timestamp(timestamp) mod 300) as ts,  
    sum(value)
from group_interval 
group by ts 
order by ts
;
mac13k
quelle
0

Ich fand heraus, dass mit MySQL wahrscheinlich die richtige Abfrage die folgende ist:

SELECT SUBSTRING( FROM_UNIXTIME( CEILING( timestamp /300 ) *300,  
                                 '%Y-%m-%d %H:%i:%S' ) , 1, 19 ) AS ts_CEILING,
SUM(value)
FROM group_interval
GROUP BY SUBSTRING( FROM_UNIXTIME( CEILING( timestamp /300 ) *300,  
                                   '%Y-%m-%d %H:%i:%S' ) , 1, 19 )
ORDER BY SUBSTRING( FROM_UNIXTIME( CEILING( timestamp /300 ) *300,  
                                   '%Y-%m-%d %H:%i:%S' ) , 1, 19 ) DESC

Lass mich wissen was du denkst.

EBAH
quelle
0
select 
CONCAT(CAST(CREATEDATE AS DATE),' ',datepart(hour,createdate),':',ROUNd(CAST((CAST((CAST(DATEPART(MINUTE,CREATEDATE) AS DECIMAL (18,4)))/5 AS INT)) AS DECIMAL (18,4))/12*60,2)) AS '5MINDATE'
,count(something)
from TABLE
group by CONCAT(CAST(CREATEDATE AS DATE),' ',datepart(hour,createdate),':',ROUNd(CAST((CAST((CAST(DATEPART(MINUTE,CREATEDATE) AS DECIMAL (18,4)))/5 AS INT)) AS DECIMAL (18,4))/12*60,2))
Gerrit Bosua
quelle
Bitte erläutern Sie Ihre Anfrage.
Daniel W.