yearweek() 查询本周数据应统一用 mode=1(周一为周首),并确保时区一致;避免在字段上使用函数导致索引失效,推荐改用日期范围查询;month() 必须配合 year() 使用,且同样建议用范围查询提升性能与准确性。

YEARWEEK() 查询本周数据时要注意周起始日
MySQL 的 YEARWEEK() 默认以周日为一周起点(mode=0),但很多业务按周一算本周,直接用 YEARWEEK(NOW()) 可能漏掉周一凌晨到周日凌晨的数据。比如今天是 2024-06-10(周一),YEARWEEK('2024-06-10') 在 mode=0 下返回的是 202424,而 YEARWEEK('2024-06-09')(周日)也返回 202424——看似同一周,实际跨了两个自然周。
实操建议:
- 统一用
mode=1:周一为每周第一天,周日为最后一天,符合 ISO 标准,推荐在 WHERE 中显式指定:YEARWEEK(create_time, 1) = YEARWEEK(NOW(), 1) - 注意时区:如果
create_time是DATETIME且存的是 UTC 时间,而服务器时区是 CST,NOW()返回的是本地时间,会导致错配——应统一转成同一时区,例如:YEARWEEK(CONVERT_TZ(create_time, '+00:00', '+08:00'), 1) = YEARWEEK(NOW(), 1) - 避免在字段上套函数:如
YEARWEEK(create_time, 1)会令索引失效;更高效的做法是计算本周起止时间后走范围查询(见下一条)
用日期范围替代 YEARWEEK() 更利于索引命中
对大表查本周数据,直接在 create_time 字段上用函数(如 YEARWEEK() 或 WEEKDAY())会让 MySQL 无法使用索引,全表扫描风险高。正确做法是算出本周一 00:00:00 和下周一是 00:00:00,然后用 BETWEEN。
示例(按周一为起点):
SELECT * FROM orders WHERE create_time >= DATE_SUB(DATE(NOW()), INTERVAL WEEKDAY(NOW()) DAY) AND create_time <p>说明:</p>
-
WEEKDAY(NOW())返回 0(周一)到 6(周日),所以DATE_SUB(DATE(NOW()), INTERVAL WEEKDAY(NOW()) DAY)得到本周一的日期 - 右边界用
而非 <code>,避免和下周一 00:00:00 数据重复计入 - 该写法中
create_time保持裸字段,B+ 树索引可生效
MONTH() 查本月数据必须搭配 YEAR() 否则跨年出错
只用 MONTH(create_time) = MONTH(NOW()) 是危险的——2023-12 和 2024-12 都满足 MONTH() = 12,结果会把去年 12 月数据也查出来。
必须同时限定年份:
- ✅ 正确:
YEAR(create_time) = YEAR(NOW()) AND MONTH(create_time) = MONTH(NOW()) - ❌ 错误:
MONTH(create_time) = MONTH(NOW())(无年份约束) - ⚠️ 注意点:如果字段类型是
DATE或DATETIME,YEAR()和MONTH()函数仍会使索引失效;同样建议改用范围查询
更优写法(范围 + 索引友好):
SELECT * FROM logs WHERE create_time >= DATE_FORMAT(NOW(), '%Y-%m-01') AND create_time <p><code>DATE_FORMAT(NOW(), '%Y-%m-01')</code> 拿到本月第一天,<code>DATE_ADD(... INTERVAL 1 MONTH)</code> 拿到下月第一天,边界清晰、无歧义、索引可用。</p><h3>时区与字段类型不一致是静默错误高发区</h3><p>最容易被忽略的是 <code>create_time</code> 字段类型和值的实际含义不匹配:</p>
- 如果是
TIMESTAMP类型,它自动按系统时区存储/读取,但显示值可能和你预期不符;DATETIME则原样存、原样读,不自动转换 - 应用层写入时用了 UTC 时间,但数据库配置了
time_zone='+08:00',导致NOW()返回 CST 时间,而create_time实际存的是 UTC——这时所有基于NOW()的计算都会偏移 8 小时 - 验证方法:执行
SELECT NOW(), @@global.time_zone, @@session.time_zone;,再查几条真实数据的create_time值,人工比对是否符合业务发生时间
没确认清楚这点之前,任何 YEARWEEK 或 MONTH 的条件都可能查出错误结果,而且很难复现——因为只有跨时区或夏令时切换前后才暴露。











