時間範囲内で5分の間隔にグループ化する


93

やりたいmySQLコマンドにいくつか問題があります。

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

これは私の現在の出力ステートメントです。

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

それらを5分間隔の結果にグループ化するにはどうすればよいですか?

出力を次のようにしたい

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 

回答:


146

これはすべての間隔で機能します。

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

ああ…mysql-flagを取得しませんでした..それはpostgresql-queryです..しかし基本的にこれはmysqlでも可能であるはずです
boecko 2010

2
わかりました..抽出の代わりに..GROUP BY round(UNIX_TIMESTAMP(timestamp)/ 300)でうまくいくはずです
boecko 2010

2
@pHiLのコメントはmySqlで正しいので、round(/)の代わりにDIVを使用する必要があります。そうしないと、間隔間の境界が間違っています
DavidC 2013年

1
いくつかのデータセットで試してみたところ、2番目のクエリはMySQLで見事に機能しました。これは、OPの懸念事項でした。@skyがないように見えるので、これについてグループのコンセンサスを得ることができますか?
Joey T

1
私もこれを試しました。2分または3分間隔、さらに5分間隔で最初のレコードが間違って表示されます。注:-最後の15分のレコードを取得するための条件を追加しました。
Ritesh 2016年

33

私は同じ問題に遭遇しました。

エポックを秒単位の分で割ってから、丸めるか、床を使用して余りを乗せるだけで、任意の分間隔でグループ化するのは簡単であることがわかりました。したがって、5分で間隔を取得したい場合は、300秒を使用します。

    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 

これにより、選択した分間隔で正しくグループ化されたデータが返されます。ただし、データが含まれていない間隔は返されません。これらの空の間隔を取得するために、関数generate_seriesを使用できます。

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

結果:

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   

ここで、出現回数がゼロの間隔で結果を取得するには、両方の結果セットを外部結合します

    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

最終結果には、値がないものも含め、5分間隔のシリーズがすべて含まれます。

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の最後のパラメーターを調整することで簡単に変更できます。この場合、「5m」を使用しますが、任意の間隔にすることができます。


1
それがMySQLだったらそうだっただろう。generate_seriesはPostgreSQL関数のようです。残念な。
アンドレアス

現在のデータのみの結果を提供する最初のクエリは、両方の期間の2つの期間の中間レコードをカウントします。10:35と10:40の2つの期間と同様に、両方のグループで10:40をカウントします。つまり、10:35から10:40と10:40から10:45に1つです。
Prem popatia

29

GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300一部のレコードが2つのグループ化された結果セットにカウントされることがわかったため、round(../ 300)の代わりに使用する必要があります。


これは正しいですラウンド(../ 300)はmySqlでそれを適切に行っていませんでした
DavidC

1
好奇心旺盛な人のためにDIV、MySQLfloor()ではBIGINTsで安全なfloat除算です。
エリックL.

1
私もこれを試しました。2分または3分間隔、さらに5分間隔で最初のレコードが間違って表示されます。注:-最後の15分のレコードを取得するための条件を追加しました。
Ritesh 2016年

丸め動作は明確に定義されておらず、使用するCライブラリに依存するため、ROUNDの代わりにTRUNCATEまたはFLOORを使用する必要があります。Lists.mysql.com/mysql/93613
MrLeeh

28

postgres、私はそれがより簡単かつ正確に使用することが判明しました

date_trunc

次のような関数:

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に指定できます。


7
@ tmarthal-賛成すべきではありません。元々の質問はmysqlに関するものでした。
buggedcom 2013

30
ここで55分間隔をどこに設定しますか?
oldgod 2013年

上記の場合、WHERE句を次のように変更します。WHEREタイムスタンプ> current_timestamp --interval '5 minutes'
Luke Smith

2
このクエリは、尋ねられたことを実行していないようです。質問は、今から5分前ではなく、「5分ごと」です。反対票を投じるのにふさわしい答え
Mohammed Rafeeq 2018

11

クエリは次のようになります。

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

おそらく、タイムスタンプをymd:HMに分割し、DIV5を使用して分を5分のビンに分割する必要があります。

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

...そして、クライアントコードの出力をfutzして、好きなように表示します。または、必要に応じて、個別の列を取得する代わりに、sqlconcat演算子を使用して日付文字列全体を作成することもできます。

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

...そしてそれをグループ化する


うーん...しかし、出力は私が取得しようとしているものを取得していません。1つの列が返され、カウントの値が何であるかはよくわかりません...
sky

2

それでも必要かどうかはわかりません。

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

これはどう:

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では、おそらく正しいクエリは次のとおりであることがわかりました。

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

どう考えているか教えてください。


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

質問の説明を入力してください。
ダニエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.