Групування за інтервалом у 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-прапор .. це запит postgresql .. але в основному це повинно бути можливим і з mysql
boecko

2
гаразд .. замість витягування .. GROUP BY round (UNIX_TIMESTAMP (timestamp) / 300) повинен зробити трюк
boecko

2
Коментар @ pHiL правильний на mySql, ви повинні використовувати DIV замість округлення (/), інакше межа між інтервалами неправильна
DavidC 16.03.13

1
Просто спробував це з декількома наборами даних, і 2-й запит чудово працює для MySQL, що стосувалося OP. Оскільки @sky, здається, відсутній, чи можемо ми отримати консенсус групи щодо цього?
Joey T

1
Я теж спробував це. він показує перший запис неправильно кожного разу, коли інтервал становить 2 хвилини або 3 хвилини, а також подальші інтервали по 5 хвилин. Примітка: - Я додав умову для отримання останніх 15 хвилин записів.
Ritesh

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 

Це поверне дані правильно групувати за вибраний інтервал хвилин; однак він не поверне інтервали, які не містять даних. Для того, щоб отримати ці порожні інтервали, ми можемо використовувати функцію generirati_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 

Інтервал можна легко змінити, налаштувавши останній параметр create_series. У нашому випадку ми використовуємо "5 м", але це може бути будь-який інтервал, який ми хочемо.


1
Це було б, якби це був MySQL. Схоже, create_series - це функція PostgreSQL. Шкода.
Андреас

Перший запит, який дає лише поточні дані, призводить лише до результату, він враховує середні записи 2 періодів часу в обох періодах часу. Як і у 2 часових періоди, 10:35 та 10:40, в обох групах вона рахує 10:40, тобто одиниця з 10:35 до 10:40 та 10:40 до 10:45.
Прем попаття

29

Вам краще скористатися GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300замість округлення (../ 300) через округлення, яке я виявив, що деякі записи підраховуються у два згруповані набори результатів.


ЦЕ правильно, раунд (../ 300) не робив цього належним чином на mySql
DavidC

1
Для тих, хто цікавиться, DIVMySQL - floor()це плаваючий підрозділ, який безпечний для BIGINTs.
Ерік Л.

1
Я теж спробував це. він показує перший запис неправильно кожного разу, коли інтервал становить 2 хвилини або 3 хвилини, а також подальші інтервали по 5 хвилин. Примітка: - Я додав умову для отримання останніх 15 хвилин записів.
Ritesh

Слід використовувати TRUNCATE або FLOOR замість ROUND, оскільки поведінка округлення недостатньо чітко визначена і залежить від використовуваної бібліотеки C. 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

30
Де ви встановлюєте 5тут для інтервалу 5 хвилин?
старшийгод

Для вищесказаного змініть речення WHERE на: WHERE мітка часу> current_timestamp - інтервал '5 хвилин'
Люк Сміт

2
Здається, цей запит не робить того, що задають, питання - "кожні 5" хвилин, а не 5 хвилин раніше. відповідь підходить для проголосування
Мохаммед Рафік

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 і скористатися DIV 5, щоб розділити хвилини на 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

... а потім виведіть результат у коді клієнта, щоб він виглядав так, як вам подобається. Або ви можете створити цілий рядок дати, використовуючи оператор sql concat замість отримання окремих стовпців, якщо хочете.

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

... а потім згрупуйтеся по цьому


Гммм ... Але результат не отримує того, що я намагаюся отримати. Він повертає один стовпець, і я не дуже впевнений, яке значення має рахунок ...
небо

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

Надайте пояснення своєму запиту.
Даніель В.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.