在业务系统里,经常需要统计或导出当天产生的数据,比如今天的注册用户、今天的交易流水。不同数据库提供的获取当前日期的函数并不一样,MySQL常用CURDATE,SQL Server常用GETDATE,如果直接拿函数来写查询,很容易因为字段类型或时区问题导致结果不准确。下面分别说明正确写法以及常见的性能坑。

MySQL中使用CURDATE查询今天记录
MySQL的CURDATE函数返回当前会话时区下的日期,格式为date,不包含时分秒。当表中的时间字段是date类型时,可以直接用等于比较。例如有一张orders表,字段create_date是date类型,查今天下单的记录可以这样写:
-- 字段为date类型,直接等于比较 SELECT * FROM orders WHERE create_date = CURDATE();
如果create_date是datetime或timestamp类型,里面带了时分秒,用等于CURDATE就会查不到,因为CURDATE是00:00:00。此时应使用DATE函数把字段转成日期再比,或者用范围查询。推荐范围写法,避免对列使用函数导致索引失效:
-- 方式一:对列用DATE函数,小表可用,大表会走不到索引 SELECT * FROM orders WHERE DATE(create_time) = CURDATE(); -- 方式二:范围查询,能正常使用create_time上的索引 SELECT * FROM orders WHERE create_time >= CURDATE() AND create_time < CURDATE() + INTERVAL 1 DAY;
从执行计划看,方式二在create_time有索引时可以做范围扫描,效率远高于方式一的全表扫描。另外CURDATE依赖会话时区,若应用连接池没统一时区,可能出现昨天23点被算成今天的情况,需要在连接串里固定serverTimezone。
SQL Server中使用GETDATE查询今天记录
SQL Server没有CURDATE,对应的是GETDATE,它返回带时分秒的datetime。直接拿GETDATE去等于某列基本查不到,因为时间精确到毫秒。正确做法是把GETDATE转成date类型,或者做范围过滤。
-- 字段是date类型 SELECT * FROM orders WHERE create_date = CAST(GETDATE() AS DATE); -- 字段是datetime类型,范围写法,可利用索引 SELECT * FROM orders WHERE create_time >= CAST(GETDATE() AS DATE) AND create_time < DATEADD(DAY, 1, CAST(GETDATE() AS DATE));
上面第二种范围写法等价于MySQL的加一天区间,能避免对create_time列使用CONVERT或CAST函数,索引依然有效。SQL Server还有SYSDATETIME等函数,但日常查今天用GETDATE足够。
需要注意,SQL Server的GETDATE取的是数据库服务器操作系统时区时间。如果服务器在UTC而业务算北京时间,就要改用GETUTCDATE再换算,或者把服务器时区调对,否则每天零点会偏移八小时。
常见错误与索引优化建议
很多人在写今天记录查询时,习惯写成WHERE DATEDIFF(day, create_time, GETDATE())=0,这种写法把列包进函数,优化器没法用索引,数据量上来后查询会非常慢。下面用表格对比几种写法:
| 数据库 | 写法 | 是否用索引 | 说明 |
|---|---|---|---|
| MySQL | DATE(create_time)=CURDATE() | 否 | 对列用函数,全表扫 |
| MySQL | create_time>=CURDATE() AND create_time<CURDATE()+1 | 是 | 范围扫描 |
| SQL Server | CONVERT(date,create_time)=CONVERT(date,GETDATE()) | 否 | 列被转换 |
| SQL Server | create_time>=CAST(GETDATE() AS date) AND create_time<DATEADD(DAY,1,...) | 是 | 范围扫描 |
总结下来,无论用CURDATE还是GETDATE,都尽量把“今天”算成一个起止区间,让字段裸列参与比较。这样既能拿到正确结果,也不会在千万级数据上把库拖垮。上线前用EXPLAIN或实际执行计划确认走了索引,才是稳妥的做法。