用GROUP BY+聚合函数做用户统计需确保分组字段与业务口径一致,过滤无效数据,用COUNT(DISTINCT user_id),避免函数索引失效;宽表构建宜先聚合再JOIN;调优需查执行计划、调配置、改分页;数据不准主因时区与延迟,须统一转换并标注时效。
如何用 GROUP BY + 聚合函数做基础用户统计
直接对用户表按维度分组是最快出数的方式,比如按注册日期、地区、来源渠道统计人数。关键不是写 SELECT,而是想清楚 GROUP BY 的字段是否和业务口径一致——比如「日活」必须用登录日志表的 DATE(login_time),不能用用户表的 created_at。
常见错误:漏掉 WHERE 过滤测试账号或无效数据。建议在统计前先加条件:WHERE status = 1 AND is_test = 0。
-
COUNT(DISTINCT user_id)是计算去重用户的核心,别只用COUNT(*) - 时间范围尽量用
BETWEEN '2024-01-01' AND '2024-01-31',避免函数包裹字段(如DATE(created_at))导致索引失效 - 如果要算留存率,得先用子查询或 CTE 拿出首日用户集,再关联后续行为表——单条 SQL 很难兼顾准确性和可读性
怎么把多张表拼成一张宽表用于报表展示
报表前端常要求「一个用户一行,带注册时间、最近登录、总订单数、首单金额」这类字段,本质是把用户主表和日志、订单、支付等事实表做左连接。但 MySQL 对大表 JOIN 效率敏感,容易慢甚至 OOM。
推荐做法:用 LEFT JOIN 配合子查询或聚合视图,而不是直接连原始明细表。例如查每个用户的最新登录时间,不要 JOIN login_log,而应先 SELECT user_id, MAX(login_time) AS last_login FROM login_log GROUP BY user_id 再 JOIN。
- 所有 JOIN 字段必须有索引,尤其是
user_id和时间字段 - 避免在 JOIN 条件里写函数,比如
ON DATE(l.login_time) = DATE(u.created_at)会强制全表扫描 - 如果报表需高频访问,考虑用定时任务把宽表结果写入
report_user_summary这类汇总表,而非每次实时计算
遇到“内存溢出”或“执行超时”怎么调优
用户统计类 SQL 最容易在数据量上 100 万后开始变慢,报错通常是 MySQL server has gone away 或 Lost connection to MySQL server during query,根本原因往往是临时表撑爆内存或排序缓冲区不足。