如何在Java/SQL中按30分钟或1小时时间间隔分组统计
按时间区间分组统计的解决方案
SQL 实现
MySQL(30分钟区间)
假设你的时间字段是entry_time(DATETIME/TIMESTAMP类型),可以通过计算将时间归到对应的30分钟区间,再分组统计:
SELECT CONCAT( DATE_FORMAT( DATE_ADD(entry_time, INTERVAL -(MINUTE(entry_time) % 30) MINUTE), '%h:%i %p' ), '-', DATE_FORMAT( DATE_ADD(entry_time, INTERVAL (30 - MINUTE(entry_time) % 30) MINUTE), '%H:%i' ) ) AS 时间区间, COUNT(*) AS 数量 FROM your_table GROUP BY 时间区间 ORDER BY 时间区间;
如果要改成1小时区间,把代码中的30替换成60即可:
SELECT CONCAT( DATE_FORMAT(entry_time, '%h:00 %p'), '-', DATE_FORMAT(DATE_ADD(entry_time, INTERVAL 1 HOUR), '%H:00') ) AS 时间区间, COUNT(*) AS 数量 FROM your_table GROUP BY DATE_FORMAT(entry_time, '%Y-%m-%d %H:00') ORDER BY 时间区间;
PostgreSQL(30分钟区间)
PostgreSQL支持DATE_TRUNC函数,实现更简洁:
SELECT CONCAT( TO_CHAR(interval_start, 'HH12:MI AM'), '-', TO_CHAR(interval_start + INTERVAL '30 minutes', 'HH24:MI') ) AS 时间区间, COUNT(*) AS 数量 FROM ( SELECT DATE_TRUNC('minute', entry_time) - INTERVAL '(EXTRACT(minute FROM entry_time) % 30) minutes' AS interval_start FROM your_table ) t GROUP BY interval_start ORDER BY interval_start;
1小时区间直接用DATE_TRUNC('hour', entry_time):
SELECT CONCAT( TO_CHAR(interval_start, 'HH12:00 AM'), '-', TO_CHAR(interval_start + INTERVAL '1 hour', 'HH24:00') ) AS 时间区间, COUNT(*) AS 数量 FROM ( SELECT DATE_TRUNC('hour', entry_time) AS interval_start FROM your_table ) t GROUP BY interval_start ORDER BY interval_start;
Java 实现(基于Stream + LocalTime)
先实现工具方法将LocalTime转换为对应区间的起始时间,再生成统一的区间字符串作为分组依据:
import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class TimeIntervalGrouping { // 定义时间格式化器 private static final DateTimeFormatter AM_PM_FORMAT = DateTimeFormatter.ofPattern("hh:mm a"); private static final DateTimeFormatter HOUR_MIN_FORMAT = DateTimeFormatter.ofPattern("HH:mm"); // 获取30分钟区间的起始时间 private static LocalTime get30MinIntervalStart(LocalTime time) { int adjustedMinutes = (time.getMinute() / 30) * 30; return LocalTime.of(time.getHour(), adjustedMinutes); } // 获取1小时区间的起始时间 private static LocalTime getHourIntervalStart(LocalTime time) { return LocalTime.of(time.getHour(), 0); } // 生成区间显示字符串 private static String getIntervalString(LocalTime start, int intervalMinutes) { LocalTime end = start.plusMinutes(intervalMinutes); return start.format(AM_PM_FORMAT) + "-" + end.format(HOUR_MIN_FORMAT); } public static void main(String[] args) { // 模拟业务数据 List<LocalTime> entryTimes = List.of( LocalTime.of(7, 0, 34), LocalTime.of(12, 30, 23), LocalTime.of(7, 23, 53), LocalTime.of(12, 46, 21), LocalTime.of(7, 10, 15), LocalTime.of(12, 35, 40) ); // 按30分钟区间分组统计 Map<String, Long> thirtyMinGroup = entryTimes.stream() .collect(Collectors.groupingBy( time -> getIntervalString(get30MinIntervalStart(time), 30), Collectors.counting() )); // 输出30分钟区间统计结果 System.out.println("时间区间\t数量"); thirtyMinGroup.forEach((interval, count) -> System.out.printf("%s\t%d%n", interval, count)); // 按1小时区间分组统计 Map<String, Long> hourGroup = entryTimes.stream() .collect(Collectors.groupingBy( time -> getIntervalString(getHourIntervalStart(time), 60), Collectors.counting() )); // 输出1小时区间统计结果 System.out.println("\n1小时区间统计:"); System.out.println("时间区间\t数量"); hourGroup.forEach((interval, count) -> System.out.printf("%s\t%d%n", interval, count)); } }
核心逻辑是通过区间起始时间生成统一的分组key,再用Collectors.counting()完成数量统计,后续可直接复用工具方法处理不同间隔的分组需求。
内容的提问来源于stack exchange,提问作者Gnanambal Meyyappan
相关产品推荐
相关产品推荐

