Pengelompokan menjadi interval 5 menit dalam satu rentang waktu


94

Saya mengalami kesulitan dengan perintah mySQL yang ingin saya lakukan.

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

Ini adalah pernyataan keluaran saya saat ini.

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

Bagaimana cara mengelompokkan mereka menjadi hasil interval 5 menit?

Saya ingin keluaran saya seperti ini

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 

Jawaban:


146

Ini bekerja dengan setiap interval.

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… tidak mendapatkan bendera mysql .. ini adalah permintaan-postgresql .. tetapi pada dasarnya ini juga dapat dilakukan dengan mysql
boecko

2
ok .. alih-alih mengekstrak .. GROUP BY putaran (UNIX_TIMESTAMP (timestamp) / 300) harus melakukan trik
boecko

2
Komentar @ pHiL benar di mySql Anda harus menggunakan DIV, bukan round (/), jika tidak, batas antara interval salah
DavidC

1
Baru saja mencobanya dengan beberapa set data dan kueri ke-2 bekerja dengan sangat baik untuk MySQL, yang merupakan perhatian OP. Karena @sky tampaknya tidak ada, dapatkah kita mendapatkan konsensus grup tentang ini adalah jawabannya?
Joey T

1
Saya telah mencoba ini juga. Ini menunjukkan rekaman pertama salah setiap interval 2 menit atau 3 menit dan selanjutnya interval 5 menit. Catatan: - Saya telah menambahkan kondisi untuk mendapatkan catatan 15 menit terakhir.
Ritesh

33

Saya menemukan masalah yang sama.

Saya menemukan bahwa itu adalah mudah untuk kelompok dengan interval menit hanya membagi zaman oleh menit dalam jumlah detik dan kemudian pembulatan atau menggunakan lantai untuk mendapatkan tumpangan dari sisanya. Jadi jika Anda ingin mendapatkan interval dalam 5 menit Anda akan menggunakan 300 detik .

    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 

Ini akan mengembalikan data dengan benar dikelompokkan dengan interval menit yang dipilih; namun, ini tidak akan mengembalikan interval yang tidak berisi data apa pun. Untuk mendapatkan interval kosong tersebut kita bisa menggunakan fungsi generate_series .

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

Hasil:

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   

Sekarang untuk mendapatkan hasil dengan interval dengan kejadian nol kita hanya menggabungkan kedua kumpulan hasil .

    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

Hasil akhirnya akan menyertakan rangkaian dengan semua interval 5 menit bahkan yang tidak memiliki nilai.

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 

Interval dapat dengan mudah diubah dengan menyesuaikan parameter terakhir dari generate_series. Dalam kasus kami, kami menggunakan '5m' tetapi bisa jadi interval apa pun yang kami inginkan.


1
Itu akan terjadi jika itu adalah MySQL. Sepertinya generate_series adalah fungsi PostgreSQL. Sangat buruk.
Andreas

Kueri pertama yang memberikan hasil hanya data saat ini saja, menghitung rekaman tengah dari 2 periode waktu di kedua periode waktu. Seperti dalam 2 periode waktu, 10:35 dan 10:40, dihitung 10:40 di kedua kelompok yaitu satu dalam 10:35 hingga 10:40 dan 10:40 hingga 10:45.
Prem popatia

29

Anda sebaiknya menggunakan GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300daripada round (../ 300) karena pembulatan saya menemukan bahwa beberapa record dihitung menjadi dua set hasil yang dikelompokkan.


Ini benar putaran (../ 300) tidak melakukannya dengan benar di mySql
DavidC

1
Bagi mereka yang penasaran, DIVMySQL adalah salah floor()satu divisi float yang aman dengan BIGINTs.
Eric L.

1
Saya telah mencoba ini juga. Ini menunjukkan rekaman pertama salah setiap interval 2 menit atau 3 menit dan selanjutnya interval 5 menit. Catatan: - Saya telah menambahkan kondisi untuk mendapatkan catatan 15 menit terakhir.
Ritesh

Seseorang harus menggunakan TRUNCATE atau FLOOR daripada ROUND karena perilaku pembulatan tidak ditentukan dengan baik dan bergantung pada library C yang digunakan. lists.mysql.com/mysql/93613
MrLeeh

28

Untuk postgres , saya merasa lebih mudah dan lebih akurat untuk menggunakan

date_trunc

fungsi, seperti:

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

Anda dapat memberikan berbagai resolusi seperti 'menit', 'jam', 'hari' dll ... ke date_trunc.


7
@tmarthal - tidak boleh diberi suara positif. Pertanyaan asli adalah untuk mysql.
buggedcom

30
Di mana Anda menyetel di 5sini untuk interval 5 menit?
oldergod

Untuk hal di atas, ubah klausa WHERE menjadi: WHERE timestamp> current_timestamp - interval '5 menit'
Luke Smith

2
Kueri ini sepertinya tidak melakukan apa yang diminta, pertanyaannya adalah 'setiap 5' menit bukan 5 menit sebelumnya. jawaban cocok untuk downvoted
Mohammed Rafeeq

11

Kueri akan menjadi seperti ini:

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

Anda mungkin harus memecah stempel waktu Anda menjadi ymd: HM dan menggunakan DIV 5 untuk membagi menit menjadi bins 5 menit - sesuatu seperti

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

... dan kemudian futz output dalam kode klien untuk tampil sesuai keinginan Anda. Atau, Anda dapat membuat seluruh string tanggal menggunakan operator sql concat daripada mendapatkan kolom terpisah, jika Anda mau.

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

... dan kemudian kelompokkan itu


Hmmm ... Tapi hasilnya tidak mendapatkan apa yang saya coba dapatkan. Ini mengembalikan satu kolom dan saya tidak begitu yakin berapa nilai hitungannya ...
langit

2

Tidak yakin apakah Anda masih membutuhkannya.

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

Bagaimana dengan yang ini:

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

Saya menemukan bahwa dengan MySQL mungkin kueri yang benar adalah sebagai berikut:

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

Biarkan aku tahu apa yang kamu pikirkan.


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

Harap berikan penjelasan atas pertanyaan Anda.
Daniel W.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.