正确写法是用范围查询:select * from orders where created_at >= curdate() and created_at
MySQL 中用
NOW()和日期函数筛选今天的数据MySQL 没有内置的 “今天” 字面量,得靠函数组合。最常用的是
DATE(NOW())或CURDATE(),两者效果一致,都返回不带时分秒的当前日期(如'2024-06-12')。关键点在于:不能直接拿created_at = CURDATE()去比,因为created_at通常是DATETIME类型,带时间部分,而CURDATE()是纯日期,隐式转换会导致索引失效或结果不准。正确写法是用范围查询:
SELECT * FROM orders WHERE created_at >= CURDATE() AND created_at
- 这个写法能走
created_at字段上的索引(如果有的话),性能好- 避免用
DATE(created_at) = CURDATE()—— 它会让字段被函数包裹,无法使用索引- 注意时区:确保 MySQL 服务端时区和业务预期一致,否则
NOW()返回的时间可能不是你认为的“今天”PostgreSQL 中用
CURRENT_DATE和时间范围PostgreSQL 更简洁:
CURRENT_DATE直接返回日期类型,但同样不能直接等值比较timestamp字段。推荐写法是:SELECT * FROM users WHERE created_at >= CURRENT_DATE AND created_at
CURRENT_DATE是日期类型,created_at是TIMESTAMP WITH TIME ZONE或不带时区的,PostgreSQL 会自动做类型兼容转换- 如果表数据量大且
created_at有索引,这个写法可以命中索引;而created_at::date = CURRENT_DATE会强制类型转换,索引失效- 时区更敏感:若字段是
TIMESTAMP WITH TIME ZONE,比较前会按数据库时区或会话时区归一化,务必确认SHOW timezone;的输出是否符合业务要求SQL Server 里小心
GETDATE()的精度和隐式转换SQL Server 的
GETDATE()返回DATETIME,自带毫秒。直接用CONVERT(DATE, GETDATE())截取日期没问题,但比较时仍要避免函数作用于列。推荐方式:SELECT * FROM logs WHERE created_at >= CAST(GETDATE() AS DATE) AND created_at
- 别写
CONVERT(DATE, created_at) = CONVERT(DATE, GETDATE())—— 列上函数导致索引不可用- SQL Server 2008+ 支持
DATE类型,用CAST(GETDATE() AS DATE)比CONVERT(VARCHAR, GETDATE(), 112)更安全、可读性更好- 如果字段是
DATETIME2,上述写法依然有效;但要注意GETDATE()返回的是DATETIME,精度到 3.33ms,而DATETIME2可达 100ns,不过对“今天”这种粒度没影响查本月记录统一用“月初到下月首日”区间
所有数据库查“本月”的逻辑本质一样:算出当月第一天(
2024-06-01)和下月第一天(2024-07-01),然后做左闭右开区间查询。这是唯一可靠、可索引、跨时区也稳定的写法。MySQL 示例:
WHERE created_at >= DATE_FORMAT(NOW(), '%Y-%m-01') AND created_at <p>PostgreSQL 示例:</p><pre class="brush:php;toolbar:false;">WHERE created_at >= DATE_TRUNC('month', NOW()) AND created_at
- 永远不要依赖
MONTH(created_at) = MONTH(NOW()) AND YEAR(created_at) = YEAR(NOW())—— 多字段计算 + 函数,索引完全失效- “本月”指日历月,不是“过去30天”,这点在财务或统计场景中容易混淆,必须明确业务定义
- 如果表有分区(比如按月分区),用
DATE_TRUNC或DATE_FORMAT生成的边界值还能帮助优化器裁剪分区真正难的不是写出语法,而是意识到:所有“日期字符串比较”“列上套函数”“忽略时区和类型隐式转换”的写法,都在悄悄拖慢查询、掩盖数据偏差。上线前最好用
EXPLAIN看一眼执行计划,确认走了索引。











