จัดกลุ่มเป็นช่วงเวลา 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 

คำตอบ:


147

สิ่งนี้ใช้ได้กับทุกช่วงเวลา

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
ตกลง .. แทนที่จะแยก .. GROUP BY รอบ (UNIX_TIMESTAMP (เวลาประทับ) / 300) ควรทำเคล็ดลับ
boecko

2
ความคิดเห็นของ @ pHiL ถูกต้องบน mySql คุณควรใช้ DIV แทนการปัดเศษ (/) มิฉะนั้นขอบเขตระหว่างช่วงเวลาจะไม่ถูกต้อง
DavidC

1
เพิ่งลองใช้กับชุดข้อมูลหลายชุดและแบบสอบถามที่ 2 ทำงานได้อย่างยอดเยี่ยมสำหรับ MySQL ซึ่งเป็นปัญหาของ OPs เนื่องจาก @sky ดูเหมือนจะไม่อยู่เราจะได้รับความเห็นพ้องเป็นกลุ่มเกี่ยวกับคำตอบนี้หรือไม่?
Joey T

1
ฉันได้ลองสิ่งนี้เช่นกัน มันแสดงบันทึกครั้งแรกผิดทุกครั้งในช่วง 2 นาทีหรือ 3 นาทีและช่วงเวลาต่อไปอีก 5 นาที หมายเหตุ: - ฉันได้เพิ่มเงื่อนไขเพื่อรับบันทึก 15 นาทีล่าสุด
พิธี

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 

ช่วงเวลาสามารถเปลี่ยนแปลงได้อย่างง่ายดายโดยการปรับพารามิเตอร์สุดท้ายของ create_series ในกรณีของเราเราใช้'5m'แต่อาจเป็นช่วงเวลาใดก็ได้ที่เราต้องการ


1
คงจะเป็นถ้าเป็น MySQL ดูเหมือนว่า create_series เป็นฟังก์ชัน PostgreSQL เลวร้ายเกินไป.
Andreas

แบบสอบถามแรกที่ให้เฉพาะข้อมูลปัจจุบันเท่านั้นจะนับระเบียนกลางของ 2 ช่วงเวลาในทั้งสองช่วงเวลา เช่นเดียวกับใน 2 ช่วงเวลา 10:35 และ 10:40 มันจะนับ 10:40 ในทั้งสองกลุ่มที่เป็นหนึ่งใน 10:35 ถึง 10:40 และ 10:40 ถึง 10:45
Prem popatia

29

คุณควรใช้GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300แทนการปัดเศษ (../ 300) เนื่องจากการปัดเศษฉันพบว่าบางระเบียนจะถูกนับเป็นสองชุดผลลัพธ์ที่จัดกลุ่ม


นี่คือรอบที่ถูกต้อง (../ 300) ทำไม่ถูกต้องบน
mySql

1
สำหรับผู้ที่อยากรู้อยากเห็นDIVใน MySQL เป็นfloor()ส่วนลอยที่ปลอดภัยด้วยBIGINTs
Eric L.

1
ฉันได้ลองสิ่งนี้เช่นกัน มันแสดงบันทึกครั้งแรกผิดทุกครั้งในช่วง 2 นาทีหรือ 3 นาทีและช่วงเวลาต่อไปอีก 5 นาที หมายเหตุ: - ฉันได้เพิ่มเงื่อนไขเพื่อรับบันทึก 15 นาทีล่าสุด
พิธี

ควรใช้ 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 นาทีที่ไหน
oldergod

สำหรับข้างต้นให้เปลี่ยน WHERE clause เป็น: WHERE timestamp> current_timestamp - interval '5 minutes'
Luke Smith

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

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;

2559-10-29 19:35:00 | 2559-10-29 19:35:50 | 4 |

2559-10-29 19:40:00 | 2559-10-29 19:40:37 | 5 |

2559-10-29 19:45:00 | 2559-10-29 19:45:09 | 6 |

2559-10-29 19:50:00 | 2559-10-29 19:51:14 | 4 |

2559-10-29 19:55:00 | 2559-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.