应使用 create_time >= CURDATE() 或 create_time >= CAST(CURDATE() AS DATETIME) 表示“从今天 00:00:00 开始”;BETWEEN 易出错,>= AND

WHERE子句中用BETWEEN还是>= AND
直接用 BETWEEN 看起来简洁,但容易漏掉边界时间点——它默认是闭区间,BETWEEN '2024-01-01' AND '2024-01-31' 会包含 '2024-01-31 00:00:00',但不包含 '2024-01-31 23:59:59'。实际业务中更常用的是左闭右开:start_time >= '2024-01-01' AND start_time 。这样既避免时区/毫秒截断问题,又天然覆盖整月所有时间戳。
常见错误现象:BETWEEN '2024-01-01' AND '2024-01-31 23:59:59' 在 PostgreSQL 或 MySQL 8.0+ 中可能因毫秒精度被截断而丢记录;SQL Server 的 datetime 类型还会四舍五入到 .000/.003/.007 秒。
- 推荐统一用
>=和组合,把“结束时间”设为下一天的 0 点 - 如果字段是
DATE类型(无时间部分),BETWEEN安全,但依然建议保持写法一致 - 注意数据库时区:查询前确认
start_time存储的是 UTC 还是本地时间,必要时用AT TIME ZONE(PostgreSQL/SQL Server)或CONVERT_TZ(MySQL)对齐
时间字段类型不匹配导致索引失效
查得慢,八成是因为用了函数包裹时间字段,比如 WHERE DATE(create_time) = '2024-01-15'。这会让数据库无法使用 create_time 上的索引,全表扫描就来了。
正确做法是把计算移到右边,让左边保持裸字段:
- 要查某天全部记录:
create_time >= '2024-01-15' AND create_time - 要查某小时:
create_time >= '2024-01-15 14:00:00' AND create_time - 避免
TO_CHAR、DATE()、CAST等函数作用于索引列
跨日查询遇到夏令时怎么办?
如果你的业务服务器和数据库部署在夏令时地区(如欧洲/北美),直接用本地时间做范围筛选,可能在春秋季某天少算或多算一小时的记录——因为系统时钟跳变,但 TIMESTAMP WITH TIME ZONE 字段不会。
关键判断点:看字段类型和存储方式:
- 用
TIMESTAMP WITH TIME ZONE(PostgreSQL)或DATETIMEOFFSET(SQL Server)存时间,并在应用层统一转成 UTC 写入,查询时也用 UTC 时间范围 - 若只能用
datetime/TIMESTAMP(无时区),务必确保所有客户端、数据库、应用配置的时区一致(例如全设为UTC),别依赖系统本地时区 - MySQL 的
datetime不带时区,timestamp存的是 UTC,读取时按 session time_zone 转换——这意味着不同连接可能看到不同时间值
MySQL里NOW()和CURDATE()选哪个?
想查“今天”的数据,别用 NOW() 做边界——它返回带时分秒的完整时间,WHERE create_time >= CURDATE() 才真正代表“从今天 00:00:00 开始”。但要注意 CURDATE() 返回的是 DATE 类型,跟 DATETIME 字段比较时会隐式转换,多数情况没问题;更稳妥写法是 create_time >= CAST(CURDATE() AS DATETIME) 或直接写字符串 '2024-01-15'。
-
NOW()适合记录“当前操作发生时刻”,不适合做范围起点 -
CURDATE()+组合可安全查“今天”:create_time >= CURDATE() AND create_time - 在 WHERE 中调用函数(如
NOW())本身不伤性能,但别把它和字段一起运算,比如DATE_SUB(NOW(), INTERVAL 1 DAY)作为右值是 OK 的
时间筛选真正难的不是语法,而是搞清字段存的是什么、数据库按什么规则解释它、你的业务要求是否包含毫秒级精度、以及上下游系统有没有悄悄做时区转换。写完语句后,一定用 EXPLAIN 看是否走了索引,再拿几条真实数据手工验算边界值。










