本周一用 DATE_SUB(NOW(), INTERVAL WEEKDAY(NOW()) DAY) 获取,本月用 DATE_FORMAT(NOW(), '%Y-%m-01') 和 LAST_DAY(NOW()) 构造范围,避免函数作用于字段导致索引失效。

查本周最新数据:用 DATE_SUB 和 NOW() 定位周一到今天
MySQL 中没有内置“本周一”函数,得靠 WEEKDAY() 或 DAYOFWEEK() 推算。更稳妥的做法是用 DATE_SUB(NOW(), INTERVAL WEEKDAY(NOW()) DAY) 得到本周一的 00:00:00,再配合 WHERE created_at >= ... 过滤。
常见错误是直接用 WEEK() 比较,它受 mode 参数影响大(比如周日算第1天还是第0天),跨年时还可能返回0或53,导致漏数据。
实操建议:
- 用
DATE_SUB(NOW(), INTERVAL WEEKDAY(NOW()) DAY)算本周一(周一为0,周日为6) - 若业务要求“本周”从周日开始,改用
DATE_SUB(NOW(), INTERVAL (DAYOFWEEK(NOW()) - 1) DAY) - 别忘了加索引:确保
created_at字段有 B-tree 索引,否则范围查询会全表扫
查本月最新数据:用 YEAR() 和 MONTH() 组合过滤最安全
用 DATE_FORMAT(created_at, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m') 看起来简洁,但会导致 created_at 列无法走索引(函数作用于字段侧)。真正高效的方式是构造起止时间范围。
实操建议:
- 起始时间:
DATE_FORMAT(NOW(), '%Y-%m-01') - 结束时间:
LAST_DAY(NOW()),再拼上' 23:59:59'(或用更精确) - 写成:
WHERE created_at >= '2024-06-01' AND created_at —— 这种写法能命中索引
查本季度最新数据:手动算 QUARTER() 容易错在跨年和边界
QUARTER(NOW()) 只返回数字 1–4,没法直接反推起始月份;有人用 CONCAT(YEAR(NOW()), '-0', QUARTER(NOW())*3-2, '-01'),但 Q1 会变成 -01,Q4 跨年时更难处理。
更稳的方法是用日期运算:
- 本季度第一天:
MAKEDATE(YEAR(NOW()), 1) + INTERVAL (QUARTER(NOW())-1)*3 MONTH - 本季度最后一天:
LAST_DAY(DATE_ADD(MAKEDATE(YEAR(NOW()), 1), INTERVAL QUARTER(NOW())*3 MONTH)) - 推荐写法:
WHERE created_at >= '2024-04-01' AND created_at (即下季度首日为上界)
注意 PostgreSQL 或 SQL Server 的 DATE_TRUNC('quarter', NOW()) 更简洁,但 MySQL 没这函数,硬套会报错。
性能与兼容性:别让 NOW() 在 WHERE 里反复计算
如果查询里多次出现 NOW()(比如同时算本周、本月、本季度),MySQL 每次都重新求值,虽影响小,但在高并发或子查询中可能引发非预期结果(例如执行过程中秒级变化)。
实操建议:
- 用变量预存:
SET @now := NOW(); SELECT ... WHERE created_at >= DATE_SUB(@now, INTERVAL WEEKDAY(@now) DAY); - 在应用层生成时间字符串传入(如 Python 的
datetime.now().replace(...)),避免数据库端复杂计算 - PostgreSQL 用户可直接用
CURRENT_DATE配合DATE_TRUNC,SQL Server 用DATEFROMPARTS(YEAR(GETDATE()), ...),语法差异大,别混用
跨数据库移植时,WEEKDAY() 和 DAYOFWEEK() 返回值相反(MySQL 中前者周一=0,后者周日=1),这个细节线上出过不少漏数据问题。

















