group by+聚合函数可高效完成基础统计报表,关键在分组维度贴合业务口径,需注意日期截断、空值处理、条件聚合替代join、物化表提升性能及导出时字符集与格式问题。

用 GROUP BY + 聚合函数做基础统计报表
绝大多数日报、周报、销售汇总类报表,靠 GROUP BY 配合 COUNT()、SUM()、AVG() 就能搞定。关键不是“能不能”,而是分组维度是否贴合业务口径——比如按日期统计,得先确认时间字段是 DATETIME 还是 DATE,否则 GROUP BY order_time 会把同一天不同小时的记录拆成多行。
- 日期截断常用:
DATE(order_time)(转为纯日期)、YEARWEEK(order_time, 1)(获取年+周序号) - 避免在
SELECT中写未分组又非聚合的字段,MySQL 5.7+ 默认拒绝这种写法(ONLY_FULL_GROUP_BY开启时) - 空值会影响
COUNT(col)(不计 NULL),但COUNT(*)会统计所有行
处理多维交叉报表:用条件聚合代替多次 JOIN
要在一个结果里同时看「各省份的订单数」和「各省份的退款金额」,别急着 LEFT JOIN 两个子查询——容易因空值或笛卡尔积导致数据膨胀。更稳的方式是用 CASE WHEN 做条件聚合:
SELECT province, COUNT(*) AS total_orders, COUNT(CASE WHEN status = 'refunded' THEN 1 END) AS refund_count, SUM(CASE WHEN status = 'refunded' THEN amount ELSE 0 END) AS refund_amount FROM orders GROUP BY province;
这样既避免关联错误,又便于后续加新指标(比如再加一列「支付成功但未发货的订单数」,只加一行 CASE 即可)。
解决实时性与性能矛盾:物化统计表 + 定时更新
当原始订单表超千万行,每次查「近30天每日销售额」都扫全表,报表页面就卡。这时硬扛索引或优化 SQL 效果有限,该换思路:
- 建一张
daily_sales_summary表,字段含stat_date DATE、total_amount DECIMAL、order_count INT - 用
INSERT ... SELECT每日凌晨跑一次,只算昨天的数据(增量更新,快且安全) - 报表查询直接读这张小表,响应从秒级降到毫秒级
- 注意:如果业务要求“实时看今天数据”,这张表需额外补上今日临时聚合(用
UNION ALL合并)
导出报表时中文乱码和格式错位问题
用 mysqldump 或 MySQL Workbench 导出 CSV 时出现乱码,大概率是字符集没对齐:
- 确认数据库/表/连接三者字符集一致,推荐统一用
utf8mb4(不是utf8) - 导出命令中必须显式指定
--default-character-set=utf8mb4 - Excel 打开 CSV 乱码?不是 MySQL 的锅——用记事本另存为 UTF-8 with BOM 格式,或改用 LibreOffice 打开
- 字段含逗号、换行符?导出时加
--fields-enclosed-by='"'和--fields-escaped-by='\',否则 Excel 会错切列
复杂报表往往卡在细节:分组逻辑漏了业务边界、导出没处理特殊字符、定时任务没设失败重试。这些地方不写进代码,但决定报表到底能不能用。










