yearweek()默认模式0易致跨年错位,应统一用模式1(iso标准);查本月宜用范围查询而非month()+year()以保索引;“今日”可用curdate()截断加interval实现;边界场景须用真实跨年/跨月数据验证。

查本周数据时,YEARWEEK() 的默认模式容易导致跨年错位
MySQL 的 YEARWEEK(date) 默认使用模式 0(周日为一周开始,且第一周需包含 1 月 1 日),这在每年年初或年末极易把“本周”算错。比如 2024-01-01 是周一,但按模式 0,它属于 2023 年第 53 周;而你用 YEARWEEK(NOW()) 拿到的是 2024 年第 1 周,两者对不上。
正确做法是统一指定模式 1:周一为每周起点,且第一周必须包含该年的第一个周四(即 ISO 标准):
SELECT * FROM orders WHERE YEARWEEK(order_time, 1) = YEARWEEK(NOW(), 1);
- 模式 1 更符合日常“本周一至周日”的认知
- 避免跨年周被拆到两个不同年份的
YEARWEEK值里 - 如果业务强制要求“周日为起点”,则用模式 0,但务必在所有地方显式传参,不可依赖默认值
查本月数据,DATE_FORMAT() 比 MONTH() + YEAR() 更安全
直接写 WHERE MONTH(create_time) = MONTH(NOW()) AND YEAR(create_time) = YEAR(NOW()) 看似直观,但会**丢失索引能力**——因为对字段做了函数运算,MySQL 无法使用 create_time 上的普通 B+Tree 索引。
改用 DATE_FORMAT() 虽也用了函数,但更推荐下面这种**范围查询写法**(不依赖函数作用于字段):
SELECT * FROM logs WHERE create_time >= DATE_FORMAT(NOW(), '%Y-%m-01') AND create_time
-
DATE_FORMAT(NOW(), '%Y-%m-01')得到当月第一天(如 2024-05-01) -
DATE_ADD(..., INTERVAL 1 MONTH)得到下月第一天,用而非 <code> 避免边界时间遗漏 - 这个写法能走
create_time索引,性能明显更好
注意 NOW() 和 CURDATE() 的时区与精度差异
如果你的表里存的是 DATETIME(无时区),但应用层或 MySQL server 设置了非 UTC 时区,NOW() 返回带时分秒的时间戳,可能导致“今天还没过完,但查询已漏掉刚插入的记录”。
更稳妥的做法是按日期粒度对齐:
- 查本周:用
DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)得到本周一,再加减 6 天构造范围(比YEARWEEK更可控) - 查本月:始终优先用
DATE_FORMAT(CURDATE(), '%Y-%m-01'),而不是NOW(),避免秒级波动影响结果稳定性 - 确认
@@time_zone和表字段类型是否匹配,尤其当字段是TIMESTAMP时,NOW()会自动转成系统时区,而CURDATE()不受时区影响
复合场景:查“本月至今”或“本周工作日”要小心逻辑嵌套
例如“本月 1 号到今天”不能简单拼 BETWEEN,因为 TODAY 可能包含时间部分,导致漏掉今天 00:00:00–00:00:59 的数据。
推荐写法:
WHERE create_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') AND create_time
- 用
CURDATE()截断时间部分,再加 1 天实现“今日结束前” - 如果要排除周末,别在 WHERE 里反复调
WEEKDAY(),先用子查询或 CTE 提取日期范围,再过滤,否则每行都计算,性能差 - 真实业务中,“本周”可能指财务周(如每周四结算),这时
YEARWEEK就完全不适用,得用偏移计算
最常被忽略的一点:无论用哪种函数,只要涉及日期计算,务必在测试库用跨月、跨年、跨周末的真实数据验证边界——比如 2024-12-30(周一)查“本周”,结果是否包含 2025-01-01?答案取决于你的模式和业务定义,代码不会替你做决策。











