DATE_FORMAT()仅接受DATE/DATETIME/TIMESTAMP类型参数,传入字符串会静默返回NULL;WHERE中使用会导致索引失效,应改用范围查询。

DATE_FORMAT() 不是“写对格式就能用”,它只吃 DATE/DATETIME/TIMESTAMP 类型,传字符串直接静默返回 NULL —— 这是 90% 报错或结果为空的根源。
DATE_FORMAT() 的参数类型必须合法
函数签名是 DATE_FORMAT(date, format),但 date 参数不是“看着像日期就行”。如果字段是 VARCHAR 或你拼了个字符串如 '2024-05-10',MySQL 不会自动转类型,而是直接返回 NULL。
- 错误写法:
DATE_FORMAT('2024-05-10', '%Y年%m月%d日')→ 返回NULL(不报错,但结果意外) - 正确写法:
DATE_FORMAT(STR_TO_DATE('2024-05-10', '%Y-%m-%d'), '%Y年%m月%d日') - 字段是字符串类型?先用
STR_TO_DATE(col, '%Y-%m-%d')转,再套DATE_FORMAT() - 字段本身是
DATETIME但含微秒?DATE_FORMAT()会自动截断,不保留.123456部分
WHERE 子句里别用 DATE_FORMAT() 做条件
在 WHERE 里对日期列调用 DATE_FORMAT(create_time, '%Y-%m'),等于告诉 MySQL:“别用索引,全表扫一遍”。因为函数作用于列,B+ 树索引失效。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 危险写法:
WHERE DATE_FORMAT(create_time, '%Y-%m') = '2024-05' - 高效替代:
WHERE create_time >= '2024-05-01' AND create_time - 真要按“星期几”过滤?优先建计算列:
ALTER TABLE t ADD COLUMN wkday TINYINT AS (WEEKDAY(create_time)) STORED,再给wkday加索引 - 注意:WHERE 中用
DATE_FORMAT()后,即使加了函数索引(MySQL 8.0+),也仅限于该确切表达式,泛化性差
格式符大小写敏感且行为反直觉
%Y 和 %y、%m 和 %c、%H 和 %h 看似只差大小写,但含义完全不同,写错会导致输出错乱或被忽略。
-
%Y是四位年(2024),%y是两位(24);%m补零(05),%c不补零(5),但%c在旧版 MySQL( -
%H是 24 小时制(00–23),%h或%I是 12 小时制(01–12),漏写%p就会把下午 2 点显示成 “02”,无法区分 AM/PM -
%w是数字星期,0=星期日;%W是英文名,如'Friday';但WEEKDAY()返回 0=星期一,混用极易对不上 - 中文“年”“月”“日”不是格式符,是字面量;想输出“五月”,得用
CASE WHEN MONTH(date_col) = 5 THEN '五月'手动映射
NULL、时区和中文输出的实际处理
DATE_FORMAT() 对 NULL 输入只返回 NULL,不报错也不提示;时区偏移、中文本地化这些事,它本身不负责,得靠你补位。
- 防 NULL:
IFNULL(DATE_FORMAT(create_time, '%Y-%m-%d'), '未知日期')或COALESCE(..., '') - 字段存的是 UTC 时间,但你要按北京时间展示?先用
CONVERT_TZ(create_time, '+00:00', '+08:00')调整,再格式化 - 想让
MONTHNAME()输出中文?它只返回英文(如'May'),必须自己CASE WHEN MONTH(...) = 5 THEN '五月' - 应用层更合适做动态格式化:比如用户时区不同、需兼容农历、要响应式格式——这类逻辑放 SQL 里反而难维护
最常被忽略的一点:DATE_FORMAT() 输出是字符串,不是日期类型。这意味着它不能直接参与日期运算、无法被 ORDER BY 高效排序(除非加函数索引)、也不能被 GROUP BY 当作时间粒度用——如果需要按“月”聚合,应该用 YEAR(create_time)*100 + MONTH(create_time) 这类数值表达式,而不是先格式化再分组。

















