MySQL时间分组应优先用DATE_FORMAT、YEARWEEK、QUARTER等函数配合GROUP BY实现,PHP仅负责预处理与时区统一;须避免拼接字符串、硬编码月份、忽略跨年周/季度逻辑,并确保索引可用。

MySQL中用DATE_FORMAT做周/月/季度分组
PHP里查周月季度汇总,本质是让MySQL按时间维度聚合,不是PHP自己算。关键在SQL的GROUP BY配合DATE_FORMAT或日期函数。直接拼接PHP变量进SQL容易出错,也危险,必须用预处理。
常见错误:用date('Y-m-d', $timestamp)在PHP里生成字符串再塞进SQL,结果时区不对、格式不匹配,或者遇到跨年周(如2024-01-01属于2023年第52周)直接崩。
- 查当月数据:
WHERE DATE_FORMAT(create_time, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') - 按自然周分组(周一为起点):
GROUP BY YEARWEEK(create_time, 1)(注意第二个参数:1=周一开头,0=周日开头) - 按季度分组:
GROUP BY CONCAT(YEAR(create_time), '-Q', QUARTER(create_time)),比用DATE_FORMAT(create_time, '%Y-Q%q')更稳(%q不是所有MySQL版本都支持)
PHP用PDO预处理避免SQL注入和时区混乱
别用mysql_query()或拼接字符串。PDO能自动绑定类型,还能统一设时区。MySQL服务端时区和PHP时区不一致时,NOW()和strtotime()结果可能差8小时。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 连接DSN里加
;charset=utf8mb4,并执行$pdo->exec("SET time_zone = '+00:00'")或跟你业务一致的时区(如'+08:00') - 查“最近7天”这类动态范围,用
DATE_SUB(NOW(), INTERVAL 6 DAY)而不是PHP生成日期字符串传进去 - 季度起止时间需计算时,用MySQL内置函数:
MAKEDATE(YEAR(NOW()), 1) + INTERVAL (QUARTER(NOW())-1) QUARTER得本季度第一天
查周汇总时小心YEARWEEK的跨年问题
YEARWEEK('2024-01-01', 1)返回202352(2023年第52周),不是202401。如果只按YEARWEEK(create_time, 1) >= YEARWEEK(NOW(), 1)查“本周及以后”,会漏掉2023年最后一周的数据。
正确做法:
- 要查“最近N周”,用
WHERE create_time >= DATE_SUB(NOW(), INTERVAL N WEEK),靠时间戳范围兜底 - 要显示“第X周”,用
CONCAT(YEARWEEK(create_time, 1) DIV 100, '年第', YEARWEEK(create_time, 1) % 100, '周'),但展示层再处理,别在WHERE里拆 - 报表导出需固定周定义时,提前确认业务规则:是ISO周(
YEARWEEK(..., 3))还是国内常用周一为始(YEARWEEK(..., 1))
季度统计避免用硬编码的1/4/7/10月
写WHERE MONTH(create_time) IN (1,2,3)查第一季度,看着简单,但无法利用索引(函数索引除外),大数据量时慢;而且跨年季度(如2023年Q4是10/11/12月)没法复用逻辑。
更可靠的方式:
- 用
QUARTER(create_time) = 1 AND YEAR(create_time) = YEAR(NOW()),字段没被函数包裹,能走索引 - 查本季度全部记录:
WHERE create_time >= DATE_SUB(DATE_SUB(NOW(), INTERVAL DAYOFYEAR(NOW())-1 DAY), INTERVAL (QUARTER(NOW())-1) QUARTER),虽然长,但精准且可索引 - 如果表有
created_at字段且已建联合索引(created_at, status),上面条件仍能命中索引前缀
时间维度统计真正难的不是写SQL,而是对齐业务定义:周从周几开始、季度是否包含财务季、跨年怎么归类。这些一旦定错,后面所有汇总都偏移,而且很难回溯校正。










