在业务数据分析场景中,按月统计数据量是非常常见的需求,比如统计每个月的订单总数、用户新增数量等。要实现这个需求,核心思路是先通过日期格式化函数将日期字段转换为年月的格式,再使用GROUP BY对格式化后的年月进行分组,最后配合聚合函数统计数量。

不同数据库的日期格式化函数差异
不同数据库提供的日期格式化函数语法不同,下面分别介绍主流数据库的实现方式。
MySQL数据库实现
MySQL中使用DATE_FORMAT函数对日期进行格式化,其中%Y代表四位年份,%m代表两位月份,组合后可以得到YYYY-MM格式的年月字符串。
-- 统计orders表每个月的订单数量,假设order_date是日期类型的订单时间字段
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS stat_month,
COUNT(*) AS order_count
FROM orders
GROUP BY stat_month
ORDER BY stat_month;
PostgreSQL数据库实现
PostgreSQL中使用TO_CHAR函数进行日期格式化,格式化参数和MySQL类似,YYYY代表四位年份,MM代表两位月份。
-- 统计user表每个月的用户注册量,假设register_time是注册时间字段
SELECT
TO_CHAR(register_time, 'YYYY-MM') AS stat_month,
COUNT(*) AS user_count
FROM user
GROUP BY stat_month
ORDER BY stat_month;
SQL Server数据库实现
SQL Server可以使用FORMAT函数或者CONVERT函数实现日期格式化,FORMAT函数的语法更直观,兼容性稍差,CONVERT函数兼容性更好。
-- 使用FORMAT函数统计每月销售额,假设sale_time是销售时间,amount是销售额
SELECT
FORMAT(sale_time, 'yyyy-MM') AS stat_month,
SUM(amount) AS total_amount
FROM sale_record
GROUP BY FORMAT(sale_time, 'yyyy-MM')
ORDER BY stat_month;
-- 使用CONVERT函数实现同样效果
SELECT
CONVERT(VARCHAR(7), sale_time, 23) AS stat_month,
SUM(amount) AS total_amount
FROM sale_record
GROUP BY CONVERT(VARCHAR(7), sale_time, 23)
ORDER BY stat_month;
常见扩展场景处理
分组后按月份排序
默认GROUP BY后的结果顺序不固定,如果需要按月份升序排列,只需要在查询末尾添加ORDER BY 格式化后的年月字段即可,如上面示例中的ORDER BY stat_month。
补全没有数据的月份
如果某个月没有对应数据,上面的查询不会返回该月份的结果,如果需要补全所有月份,需要先生成连续的月份序列,再左连接统计结果。下面是一个MySQL的实现示例:
-- 生成最近12个月的月份序列,左连接订单统计结果,没有数据的月份显示0
WITH month_series AS (
SELECT DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL n MONTH), '%Y-%m') AS stat_month
FROM (
SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
) t
)
SELECT
ms.stat_month,
COALESCE(o.order_count, 0) AS order_count
FROM month_series ms
LEFT JOIN (
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS stat_month,
COUNT(*) AS order_count
FROM orders
GROUP BY stat_month
) o ON ms.stat_month = o.stat_month
ORDER BY ms.stat_month;
注意事项
- 日期字段如果不是日期类型,需要先使用对应数据库的日期转换函数转为日期类型再格式化,比如MySQL的
STR_TO_DATE,PostgreSQL的TO_DATE。 - GROUP BY子句中可以直接使用格式化后的表达式,也可以使用别名,不同数据库对别名的支持有差异,建议使用表达式保证兼容性。
- 如果统计的日期范围很大,建议先通过WHERE条件过滤日期范围,减少数据扫描量,提升查询性能。