date_format函数在mysql中正确写法为date_format(date_expr, format_string),首参须为日期类型,次参为格式模板,混用%符号或传入非日期字符串易出错且影响索引性能。

DATE_FORMAT函数在MySQL里怎么写才不出错
DATE_FORMAT不是标准SQL函数,只在MySQL和MariaDB中可用,其他数据库比如PostgreSQL要用TO_CHAR,SQL Server用FORMAT或CONVERT。直接在非MySQL环境里写DATE_FORMAT会报错FUNCTION does not exist。
正确用法是:DATE_FORMAT(date_expr, format_string),第一个参数必须是日期类型(DATE、DATETIME或TIMESTAMP),传字符串会隐式转换但不可靠;第二个参数是格式模板,不能拼错字符——比如%Y是4位年份,%y是2位,%m是补零月,%c是不补零月,混用会导致结果意外。
- 常见错误:把
NOW()直接塞进DATE_FORMAT当第一个参数没问题,但若字段本身是VARCHAR存的“2023-10-05”,得先用STR_TO_DATE转成日期再格式化,否则返回NULL - 格式字符串里普通字符(如横线、空格)要原样写,不需要转义;但百分号
%必须成对出现,单个%会中断解析 - 时区不敏感:
DATE_FORMAT只处理值本身,不自动适配连接会话时区,如果原始时间是UTC而你按本地时区理解,结果会偏移
常见日期格式需求对应的format_string怎么选
不同业务场景需要不同输出,关键不是记全所有符号,而是抓住几个高频组合:
- “2023-10-05” →
'%Y-%m-%d'(注意是小写m,大写M是分钟) - “2023/10/05 14:30” →
'%Y/%m/%d %H:%i'(%H是24小时制,%h是12小时制;%i是分钟,%s是秒) - “Oct 05, 2023” →
'%b %d, %Y'(%b是英文缩写,%M是全名,中文环境默认不支持中文月份名) - “星期四 2023年10月5日” →
DATE_FORMAT做不到,它不支持本地化 weekday/month 名称;得用应用层拼接或MySQL 8.0+配合lc_time_names变量临时设为'zh_CN'(但仍有局限)
DATE_FORMAT和类型转换一起用时的坑
很多人想把格式化后的字符串再转成日期做比较,比如:WHERE DATE_FORMAT(create_time, '%Y-%m') = '2023-10'。这看起来方便,但实际会破坏索引——因为对字段用了函数,MySQL无法走create_time上的索引,哪怕加了函数索引(MySQL 8.0+)也只对精确匹配有效,范围查询仍失效。
- 替代方案:用范围查询代替格式化,例如查2023年10月全部记录,写成
create_time >= '2023-10-01' AND create_time - 如果真要按年月分组统计,
GROUP BY YEAR(create_time), MONTH(create_time)比GROUP BY DATE_FORMAT(create_time, '%Y-%m')更高效且可利用索引 -
DATE_FORMAT(NOW(), '%Y%m%d')这类用于生成文件名或分区键的场景没问题,但别把它当条件字段反复计算
在SELECT之外的地方能不能用DATE_FORMAT
可以,但有限制:在ORDER BY、GROUP BY、HAVING里都能用,不过要注意语义是否合理。比如ORDER BY DATE_FORMAT(create_time, '%H:%i')会按“时间字符串”排序,不是按真实时间顺序——因为“09:05”字典序小于“10:00”,但“9:05”和“10:00”本身没问题;真正出问题的是像'%m/%d'这种,会导致“12/1”排在“1/10”前面。
- 在
WHERE子句里用要格外小心,如前文所说,基本等于放弃索引 - 不能在
DEFAULT约束或生成列定义里直接调用DATE_FORMAT(MySQL报错Invalid default value),生成列只能用确定性函数,而DATE_FORMAT被认定为非确定性(尽管实际是) - 视图定义里可以用,但下游应用如果依赖该字段做二次筛选,性能隐患会继承过去
真正麻烦的不是语法会不会写,而是什么时候不该用——尤其当表数据量过万后,一个DATE_FORMAT套在WHERE里,可能让原本毫秒级的查询变成秒级。










