시간 범위 내에서 5 분 간격으로 그룹화


95

수행하려는 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 

답변:


148

이것은 모든 간격에서 작동합니다.

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

2
ok .. 추출 대신 .. GROUP BY round (UNIX_TIMESTAMP (timestamp) / 300) 트릭을 수행해야합니다
boecko

2
@pHiL의 주석은 mySql에서 정확합니다. round (/) 대신 DIV를 사용해야합니다. 그렇지 않으면 간격 사이의 경계가 잘못되었습니다
DavidC

1
여러 데이터 세트로 시도하고 두 번째 쿼리는 MySQL에서 훌륭하게 작동하며 OP의 관심사였습니다. @sky가 결석 한 것 같기 때문에 이것이 답이 될 것이라는 그룹 합의를 얻을 수 있습니까?
조이 T

1
나는 이것도 시도했다. 2 분 또는 3 분 간격과 5 분 간격마다 첫 번째 기록이 잘못 표시됩니다. 참고 :-지난 15 분 기록을 가져 오는 조건을 추가했습니다.
Ritesh

34

나는 같은 문제를 만났습니다.

나는 어떤 분 간격으로 그룹화하기 쉽다는 것은 단지 epoch 를 초 단위로 분으로 나눈 다음 반올림하거나 바닥을 사용하여 나머지를 타는 것입니다. 따라서 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 함수 인 것 같습니다. 너무 나쁘다.
Andreas

현재 데이터 만 제공하는 첫 번째 쿼리는 두 기간 모두에서 2 개 기간의 중간 레코드를 계산합니다. 2 개의 기간 인 10:35 및 10:40과 마찬가지로 10:35에서 10:40 및 10:40에서 10:45 중 하나 인 두 그룹 모두에서 10:40을 계산합니다.
Prem popatia

29

GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300round (../ 300) 대신 사용 하는 것이 좋습니다 . 반올림 때문에 일부 레코드가 두 개의 그룹화 된 결과 집합으로 계산된다는 것을 알았습니다.


이것은 정확합니다 라운드 (
../300

1
궁금한 사람들을 위해 DIVMySQL floor()에서는 BIGINTs로 안전한 부동 분할입니다 .
Eric L.

1
나는 이것도 시도했다. 2 분 또는 3 분 간격과 5 분 간격마다 첫 번째 기록이 잘못 표시됩니다. 참고 :-지난 15 분 기록을 가져 오는 조건을 추가했습니다.
Ritesh

반올림 동작이 잘 정의되어 있지 않고 사용 된 C 라이브러리에 따라 다르기 때문에 ROUND 대신 TRUNCATE 또는 FLOOR를 사용해야합니다. lists.mysql.com/mysql/93613
MrLeeh

28

들어 포스트 그레스 , 나는 쉽게하고를 사용하는 것이 더 정확 발견

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
여기서 55 분 간격으로 설정하는 곳은 어디 입니까?
oldergod

위의 경우 WHERE 절을 다음과 같이 변경하십시오. WHERE timestamp> current_timestamp-간격 '5 분'
Luke Smith

2
이 쿼리는 요청 된 작업을 수행하지 않는 것 같습니다. 질문은 지금 5 분 전이 아니라 '매 5'분입니다. 응답 맞춤은을 downvoted 수
모하메드 라 피크를

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

... 그런 다음 그룹화


흠 ...하지만 출력이 내가 원하는 것을 얻지 못합니다. 하나의 열을 반환하고 개수 값이 무엇인지 잘 모르겠습니다 ...
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))

질문에 대한 설명을 입력하십시오.
Daniel W.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.