date_format用于格式化日期输出为字符串,不改变原值类型;now返回当前时间戳,语句内固定。二者需注意类型转换、索引失效、大小写敏感及null处理。

SQL 中的日期处理是日常开发和数据分析中的高频需求,DATE_FORMAT 和 NOW 是 MySQL 里最常用、也最容易用错的两个日期相关函数。掌握它们的正确用法,能帮你快速提取、格式化、比对时间数据,避免因时区、格式、类型隐式转换导致的查询结果偏差。
DATE_FORMAT:按需定制日期显示格式
DATE_FORMAT 不改变字段真实值,只控制输出样式。它的核心是格式符(format specifiers),比如 %Y 表示 4 位年份,%m 是补零月,%d 是补零日。常见误区是把 DATE_FORMAT 当作类型转换函数——它返回的是字符串,不是日期类型,不能直接用于日期运算或索引扫描。
- 正确用法示例:SELECT DATE_FORMAT(create_time, '%Y-%m') AS month_str FROM orders; —— 提取年月字符串,适合分组统计
- 错误写法示例:WHERE DATE_FORMAT(create_time, '%Y-%m') = '2024-05' —— 会丢失索引,应改用范围查询:create_time >= '2024-05-01' AND create_time
- 注意中文环境下的星期/月份名:默认返回英文,如需中文需配合 SET lc_time_names = 'zh_CN'(会话级生效)
NOW:获取当前时间,但要注意时区与精度
NOW() 返回服务器当前时间戳(datetime 类型),精确到秒;NOW(3) 可带小数秒精度(如毫秒)。它的值在语句执行开始时确定,同一 SQL 内多次调用返回相同结果,适合做一致性快照。
- 对比其他函数:CURDATE() 只返回日期部分('2024-05-20'),CURTIME() 只返回时间部分('14:30:22')
- 插入记录时自动记录创建时间:INSERT INTO logs(msg, created_at) VALUES ('error', NOW());
- 时区陷阱:如果应用跨时区部署,建议统一使用 UTC 时间存储,用 UTC_TIMESTAMP() 替代 NOW(),避免夏令时或服务器时区配置不一致引发的问题
组合实战:按日/周/月统计活跃用户
结合 DATE_FORMAT 和 NOW,可以写出简洁高效的聚合查询。关键是让格式化逻辑服务于分组条件,同时避免在 WHERE 中对日期字段做函数操作。
- 查最近7天每日新增用户数:SELECT DATE_FORMAT(reg_time, '%Y-%m-%d') day, COUNT(*) cnt FROM users WHERE reg_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY day ORDER BY day;
- 查本周(周一至周日)订单总额:SELECT SUM(amount) FROM orders WHERE YEARWEEK(reg_time, 1) = YEARWEEK(NOW(), 1);(参数1表示周一为每周第一天)
- 查上个月数据(推荐):WHERE reg_time >= DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 1 MONTH), '%Y-%m-01') AND reg_time —— 利用边界范围,可走索引
避坑提醒:几个高频错误场景
实际写 SQL 时,这些细节常被忽略,却直接影响性能和结果准确性:
- 别在 WHERE 中对日期字段用 DATE_FORMAT:会导致全表扫描,改用 BETWEEN 或 >= /
- NOW() 在 INSERT ... SELECT 或存储过程中行为一致:整条语句内时间戳固定,不会随行变化
- DATE_FORMAT 的格式符大小写敏感:%Y ≠ %y(前者是4位年,后者是2位),%H 是24小时制,%h 是12小时制
- NULL 值处理:DATE_FORMAT(NULL, '%Y') 返回 NULL,不影响 GROUP BY,但需在业务层判断是否过滤










