Grupowanie w interwał 5 minut w zakresie czasu


97

Mam pewne problemy z poleceniami mySQL, które chcę wykonać.

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

To jest moje aktualne oświadczenie wyjściowe.

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

Jak pogrupować je w wyniki co 5 minut?

Chcę, żeby moja twórczość była taka

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 

Odpowiedzi:


150

Działa to z każdym interwałem.

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 
WHEREGROUP BY 
round(extract('epoch' from timestamp) / 300), name


MySQL

SELECT
    timestamp,  -- not sure about that
    name,
    count(b.name)
FROM time a, id 
WHEREGROUP BY 
UNIX_TIMESTAMP(timestamp) DIV 300, name

oh… nie dostałem flagi mysql .. to jest zapytanie postgresql .. ale w zasadzie powinno to być również możliwe z mysql
boecko

2
ok .. zamiast wypakować .. GROUP BY round (UNIX_TIMESTAMP (timestamp) / 300) powinno załatwić
sprawę

2
Komentarz @ pHiL do mySql jest poprawny, powinieneś użyć DIV zamiast round (/), w przeciwnym razie granica między interwałami jest nieprawidłowa
DavidC

1
Właśnie wypróbowałem to z kilkoma zestawami danych, a drugie zapytanie działa doskonale w przypadku MySQL, co było problemem dla PO. Skoro @sky wydaje się nieobecny, czy możemy uzyskać konsensus grupy co do tego, że jest to odpowiedź?
Joey T

1
Ja też tego próbowałem. pokazuje nieprawidłowy pierwszy rekord za każdym razem co 2 minuty lub 3 minuty i kolejne 5 minut. Uwaga: - dodałem warunek, aby uzyskać rekordy z ostatnich 15 minut.
Ritesh

37

Natknąłem się na ten sam problem.

Zauważyłem, że łatwo jest grupować według dowolnych minutowych interwałów, po prostu dzieląc epokę przez minuty w ilości sekund, a następnie albo zaokrąglając, albo używając podłogi, aby uzyskać przejechanie pozostałej części. Więc jeśli chcesz uzyskać interwał za 5 minut , użyjesz 300 sekund .

    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 

To zwróci dane poprawnie pogrupowane według wybranych minut; jednak nie zwróci interwałów, które nie zawierają żadnych danych. Aby uzyskać te puste interwały, możemy użyć funkcji gene_series .

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

Wynik:

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   

Teraz, aby otrzymać wynik z interwałem z zerową liczbą wystąpień, po prostu łączymy zewnętrzne zestawy wyników .

    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

Wynik końcowy będzie obejmował serie ze wszystkimi pięciominutowymi interwałami, nawet te, które nie mają wartości.

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 

Interwał można łatwo zmienić, dostosowując ostatni parametr generowania_serii. W naszym przypadku używamy „5m”, ale może to być dowolny interwał .


3
Byłoby, gdyby to był MySQL. Wygląda na to, że generuj_series to funkcja PostgreSQL. Szkoda.
Andreas

Pierwsze zapytanie, które daje tylko aktualne dane, liczy rekordy środkowe z 2 okresów w obu okresach. Podobnie jak w 2 przedziałach czasowych, 10:35 i 10:40, w obu grupach liczy się 10:40, czyli jedna od 10:35 do 10:40 i od 10:40 do 10:45.
Prem popatia

29

Powinieneś raczej używać GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300zamiast round (../ 300), ponieważ zauważyłem, że niektóre rekordy są zliczane w dwa zgrupowane zestawy wyników.


To jest poprawne runda (../ 300) nie robiła tego poprawnie na mySql
DavidC

1
Dla ciekawskich, DIVw MySQL jest floor()podział typu float, który jest bezpieczny dzięki BIGINTs.
Eric L.

1
Ja też tego próbowałem. pokazuje nieprawidłowy pierwszy rekord za każdym razem co 2 minuty lub 3 minuty i kolejne 5 minut. Uwaga: - dodałem warunek, aby uzyskać rekordy z ostatnich 15 minut.
Ritesh

Należy użyć TRUNCATE lub FLOOR zamiast ROUND, ponieważ zachowanie zaokrąglania nie jest dobrze zdefiniowane i zależy od używanej biblioteki C. lists.mysql.com/mysql/93613
MrLeeh

28

W przypadku postgres okazało się, że łatwiejsze i dokładniejsze jest używanie

date_trunc

funkcja, na przykład:

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

Możesz podać różne rozdzielczości, takie jak „minuta”, „godzina”, „dzień” itp. Do date_trunc.


7
@tmarthal - nie należy go popierać. Pierwotne pytanie dotyczyło mysql.
buggedcom

30
Gdzie ustawiasz 5tutaj interwał 5 minut?
oldgod

W powyższym przypadku zmień klauzulę WHERE na: WHERE timestamp> current_timestamp - interwał '5 minut'
Luke Smith

2
Wydaje się, że to zapytanie nie spełnia tego, co zostało zadane, pytanie brzmi „co 5 minut”, a nie 5 minut wcześniej. odpowiedź godna odrzucenia
Mohammed Rafeeq

11

Zapytanie będzie wyglądać mniej więcej tak:

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

4

Prawdopodobnie będziesz musiał podzielić swój znacznik czasu na ymd: HM i użyć DIV 5, aby podzielić minuty na 5-minutowe pojemniki - coś w rodzaju

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

... a następnie wyświetl wynik w kodzie klienta, aby wyglądał tak, jak lubisz. Lub możesz zbudować cały ciąg daty za pomocą operatora concat sql zamiast pobierania oddzielnych kolumn, jeśli chcesz.

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'))

... a potem pogrupuj to


Hmmm ... Ale wynik nie daje tego, co próbuję uzyskać. Zwraca jedną kolumnę i nie jestem pewien, jaka jest wartość licznika ...
niebo

2

Nie jestem pewien, czy nadal go potrzebujesz.

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

29.10.2016 19:35:00 | 29.10.2016 19:35:50 | 4 |

29.10.2016 19:40:00 | 29.10.2016 19:40:37 | 5 |

29.10.2016 19:45:00 | 29.10.2016 19:45:09 | 6 |

29.10.2016 19:50:00 | 29.10.2016 19:51:14 | 4 |

29.10.2016 19:55:00 | 29.10.2016 19:56:17 | 1 |


1

A co z tym:

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

0

Dowiedziałem się, że w przypadku MySQL prawdopodobnie poprawne zapytanie jest następujące:

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

Powiedz mi co myślisz.


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))

Podaj wyjaśnienie swojego zapytania.
Daniel W.

0

Pomoże to dokładnie w tym, czego chcesz

wymień dt - twoją datę i godzinę c - wywołanie pola astro_transit1 - twoja tabela 300 odwołaj się 5 minut, więc dodaj 300 za każdym razem, aby zwiększyć odstęp czasowy

SELECT FROM_UNIXTIME( 300 * ROUND( UNIX_TIMESTAMP( r.dt ) /300 ) ) AS 5datetime, (
SELECT r.c
FROM astro_transit1 ra
WHERE ra.dt = r.dt
ORDER BY ra.dt DESC
LIMIT 1
) AS first_val FROM astro_transit1 r GROUP BY UNIX_TIMESTAMP( r.dt )
DIV 300
LIMIT 0 , 30
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.