Группировка в интервал 5 минут во временном диапазоне

94

У меня есть некоторые трудности с командами 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
Boecko
источник
о ... не получил mysql-flag .. это postgresql-query .. но в основном это должно быть возможно и с mysql
boecko 03
2
хорошо .. вместо извлечения .. GROUP BY round (UNIX_TIMESTAMP (timestamp) / 300) должно помочь
boecko
2
Комментарий @ pHiL верен на mySql, вы должны использовать DIV вместо round (/), иначе граница между интервалами неправильная
DavidC
1
Просто попробовал это с несколькими наборами данных, и второй запрос блестяще работает для 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 

Это вернет данные, правильно сгруппированные по выбранному интервалу минут; однако он не вернет интервалы, которые не содержат никаких данных. Чтобы получить эти пустые интервалы, мы можем использовать функцию 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. В нашем случае мы используем «5 минут», но это может быть любой интервал, который мы хотим.

Нестор Мартинес
источник
1
Было бы, если бы это был MySQL. Похоже, generate_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
Для тех, кому интересно, DIVв MySQL есть 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 timestamp> current_timestamp - interval '5 minutes'
Люк Смит
2
Этот запрос, похоже, не выполняет то, что задают, вопрос - «каждые 5 минут», а не за 5 минут до этого. ответ заслуживает отрицательного голосования
Mohammed Rafeeq
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
WASD42
источник
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 |

user7088930
источник
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
;
mac13k
источник
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

Дайте мне знать, что вы думаете.

EBAH
источник
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))
Геррит Босуа
источник
Пожалуйста, дайте объяснение вашему запросу.
Daniel W.