Bir zaman aralığında 5 dakikalık aralıklarla gruplama


94

MySQL komutlarıyla yapmak istediğim bazı zorluklar yaşıyorum.

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

Bu benim şu anki çıktı ifadem.

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

Bunları 5 dakikalık aralık sonuçlarına nasıl gruplayabilirim?

Çıktımın şöyle olmasını istiyorum

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 

Yanıtlar:


146

Bu her aralıkta çalışır.

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… mysql bayrağını alamadım .. bu bir postgresql-sorgusu .. ama temelde bu mysql ile de mümkün olmalı
boecko

2
tamam .. ayıklamak yerine .. GROUP BY raund (UNIX_TIMESTAMP (zaman damgası) / 300) işe yaramalı
boecko

2
Phil comment mySql üzerinde doğru @ yerine yuvarlak bir DIV kullanmalıdır (/) aksi aralıklarla arasındaki Boundry yanlıştır
DavidC

1
Birkaç veri kümesiyle denedim ve 2. sorgu, OP'lerin endişesi olan MySQL için mükemmel çalışıyor. @Sky yok gibi göründüğüne göre, bu sorunun cevabı üzerinde grup fikir birliğine varabilir miyiz?
Joey T

1
Bunu ben de denedim. ilk kaydı her 2 dakika veya 3 dakika arayla ve 5 dakika daha aralıklarla yanlış gösteriyor. Not: - Son 15 dakikalık kayıtları almak için bir koşul ekledim.
Ritesh

33

Ben de aynı sorunla karşılaştım.

Herhangi bir dakika aralığına göre gruplamanın kolay olduğunu buldum, dönemi sadece saniye miktarında dakikalara bölerek ve sonra geri kalanı sürmek için yuvarlama veya zemini kullanma. Yani 5 dakika içinde aralık almak istiyorsanız 300 saniye kullanırsınız .

    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 

Bu, verileri seçilen dakika aralığına göre doğru şekilde gruplandıracaktır; ancak, herhangi bir veri içermeyen aralıkları döndürmez. Bu boş aralıkları elde etmek için, generate_series işlevini kullanabiliriz .

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

Sonuç:

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   

Şimdi sonucu sıfır oluşumlu aralıklarla elde etmek için her iki sonuç kümesini de dıştan birleştiriyoruz .

    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

Nihai sonuç, hiçbir değeri olmayanlar bile 5 dakikalık aralıkların tümünü içeren serileri içerecektir.

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 

Generate_series'in son parametresi ayarlanarak aralık kolayca değiştirilebilir. Bizim durumumuzda '5m' kullanıyoruz, ancak istediğimiz herhangi bir aralık olabilir .


1
MySQL olsaydı olurdu. Generate_series bir PostgreSQL işlevi gibi görünüyor. Çok kötü.
Andreas

Yalnızca mevcut verileri veren ilk sorgu, her iki dönemdeki 2 zaman döneminin orta kayıtlarını sayar. 2 zaman diliminde olduğu gibi, 10:35 ve 10:40, her iki grupta da 10:40 sayılır, 10:35 - 10:40 ve 10:40 - 10:45.
Prem popatia

29

GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300Yuvarlak (../ 300) yerine kullanmayı tercih etmelisiniz çünkü yuvarlama nedeniyle bazı kayıtların iki gruplanmış sonuç kümesinde sayıldığını gördüm.


Bu MySQL düzgün yapmıyordum yuvarlak (../ 300) doğru olduğundan
DavidC

1
Merak edenler için, DIVMySQL bir olduğu floor()ile güvenli bir şamandıra bölünme BIGINTs.
Eric L.

1
Bunu ben de denedim. ilk kaydı her 2 dakika veya 3 dakika arayla ve 5 dakika daha aralıklarla yanlış gösteriyor. Not: - Son 15 dakikalık kayıtları almak için bir koşul ekledim.
Ritesh

YUVARLAK yerine TRUNCATE veya FLOOR kullanılmalıdır çünkü yuvarlama davranışı iyi tanımlanmamıştır ve kullanılan C kitaplığına bağlıdır. lists.mysql.com/mysql/93613
MrLeeh

28

İçin Postgres'e , ben daha kolay ve kullanımı daha doğru buldum

date_trunc

işlev, örneğin:

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

Date_trunc'a kadar 'dakika', 'saat', 'gün' vb. Gibi çeşitli çözünürlükler sağlayabilirsiniz.


7
@tmarthal - yükseltilmemelidir. Asıl soru mysql içindi.
buggedcom

30
Burada 55 dakikalık aralık için nereye ayarlıyorsunuz ?
oldergod

Yukarıdakiler için, WHERE cümlesini şu şekilde değiştirin: WHERE timestamp> current_timestamp - interval '5 minutes'
Luke Smith

2
Bu sorgu sorulan şeyi yapmıyor gibi görünüyor, soru şu andan 5 dakika önce değil, '5 dakikada bir'. cevap olumsuz oylanmaya uygun
Muhammed Rafeeq

11

Sorgu şöyle bir şey olacaktır:

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

Muhtemelen zaman damganızı ymd: HM'ye ayırmanız ve DIV 5'i dakikaları 5 dakikalık bölmelere ayırmak için kullanmanız gerekecek.

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

... ve sonra çıktıyı istediğiniz gibi görünmesi için istemci kodunda futz edin. Veya isterseniz ayrı sütunlar almak yerine sql concat operatörünü kullanarak tüm tarih dizesini oluşturabilirsiniz.

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

... ve sonra bunun üzerine grupla


Hmmm ... Ama çıktı elde etmeye çalıştığım şeyi almıyor. Bir sütun döndürüyor ve sayının değerinin ne olduğundan pek emin değilim ...
gökyüzü

2

Hala ihtiyacınız olup olmadığından emin değilim.

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 |


1

Buna ne dersin:

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

MySQL ile muhtemelen doğru sorgunun şu olduğunu öğrendim:

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

Ne düşündüğü söyle.


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

Lütfen sorunuza bir açıklama yapın.
Daniel W.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.