在业务系统开发中,按时间维度统计数据是非常常见的需求,比如统计每日新增用户数、每月销售额、每小时接口调用次数等。MySQL内置了多种日期时间处理函数,配合分组和聚合语法就能高效实现这类统计需求。
常用时间统计函数介绍
MySQL的日期时间函数是按时间统计的核心,以下是几个最常用的函数:
- DATE():提取日期时间的日期部分,比如将
2024-05-20 14:30:00转换为2024-05-20,适合按天统计。 - YEAR():提取日期时间的年份部分,用于按年统计。
- MONTH():提取日期时间的月份部分,用于按月统计。
- HOUR():提取日期时间的小时部分,用于按小时统计。
- DATE_FORMAT():自定义时间格式,支持更灵活的时间粒度划分,比如按周、按季度统计。
基础按时间统计示例
假设我们有一张订单表order_info,表结构如下:
-- 订单表结构
CREATE TABLE order_info (
id INT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(32) NOT NULL,
order_amount DECIMAL(10,2) NOT NULL,
create_time DATETIME NOT NULL
);
按天统计订单量和总金额
使用DATE()函数提取创建时间的日期部分,再按日期分组统计:
-- 按天统计每日订单数和总金额
SELECT
DATE(create_time) AS stat_date,
COUNT(*) AS order_count,
SUM(order_amount) AS total_amount
FROM order_info
GROUP BY DATE(create_time)
ORDER BY stat_date ASC;
按月统计订单数据
使用YEAR()和MONTH()函数组合,实现按自然月统计:
-- 按月统计每月订单数和总金额
SELECT
YEAR(create_time) AS stat_year,
MONTH(create_time) AS stat_month,
COUNT(*) AS order_count,
SUM(order_amount) AS total_amount
FROM order_info
GROUP BY YEAR(create_time), MONTH(create_time)
ORDER BY stat_year ASC, stat_month ASC;
按小时统计订单数据
如果需要统计一天内每小时的订单情况,可以使用HOUR()函数:
-- 按小时统计每小时订单数
SELECT
HOUR(create_time) AS stat_hour,
COUNT(*) AS order_count
FROM order_info
WHERE DATE(create_time) = '2024-05-20' -- 统计指定日期的数据
GROUP BY HOUR(create_time)
ORDER BY stat_hour ASC;
灵活时间粒度统计
当需要按周、按季度等更灵活的时间粒度统计时,可以使用DATE_FORMAT()函数自定义时间格式:
| 统计粒度 | DATE_FORMAT格式字符串 |
|---|---|
| 按周统计 | %Y-%u(%Y是年,%u是一年中的周数,周一为周起始) |
| 按季度统计 | %Y-Q%q(%q是季度,1到4) |
| 按半小时统计 | %Y-%m-%d %H:%i(将分钟部分按30取整需要额外处理) |
按周统计示例
-- 按周统计每周订单数
SELECT
DATE_FORMAT(create_time, '%Y-%u') AS stat_week,
COUNT(*) AS order_count
FROM order_info
GROUP BY DATE_FORMAT(create_time, '%Y-%u')
ORDER BY stat_week ASC;
按半小时统计示例
按半小时统计需要将分钟部分归到0或者30,可以通过计算实现:
-- 按半小时统计每小时的订单分布
SELECT
CONCAT(
DATE_FORMAT(create_time, '%Y-%m-%d %H:'),
IF(MINUTE(create_time) < 30, '00', '30')
) AS stat_half_hour,
COUNT(*) AS order_count
FROM order_info
GROUP BY stat_half_hour
ORDER BY stat_half_hour ASC;
统计时间范围补全技巧
实际业务中经常需要统计连续时间范围的数据,如果某天没有数据,分组统计会缺失该天的记录。可以通过生成连续时间序列表再左连接统计结果的方式补全:
-- 生成最近7天的连续日期,统计每日订单数,无数据的天显示0
WITH RECURSIVE date_range AS (
SELECT CURDATE() - INTERVAL 6 DAY AS stat_date
UNION ALL
SELECT stat_date + INTERVAL 1 DAY
FROM date_range
WHERE stat_date < CURDATE()
)
SELECT
dr.stat_date,
COALESCE(COUNT(oi.id), 0) AS order_count
FROM date_range dr
LEFT JOIN order_info oi ON DATE(oi.create_time) = dr.stat_date
GROUP BY dr.stat_date
ORDER BY dr.stat_date ASC;
上述代码中COALESCE函数用于将空值转换为0,保证没有数据的日期也能正常显示统计结果。