str_to_date要求格式符与字符串逐位严格匹配,错配静默返回null;需用%c/%e处理无前导零、%y/%y区分年份位数、清洗中文、避免全表函数计算,并推荐生成列+索引优化查询性能。

STR_TO_DATE 不能直接解析无分隔符的8位数字字符串
比如 '20230101' 这种格式,直接用 STR_TO_DATE('20230101', '%Y%m%d') 是可以的,但很多人误以为 '2023-1-1' 或 '23/01/01' 也能靠猜——MySQL 不会自动补零或推断分隔符含义,必须严格匹配格式符。
常见错误现象:STR_TO_DATE('2023-1-1', '%Y-%m-%d') 返回 NULL,因为 %m 要求两位月份数字('01'),而 '1' 不匹配。
- 用
%c替代%m处理非零填充的月份('1'、'12'都可匹配) - 用
%e替代%d处理非零填充的日期('1'、'15'都可匹配) - 年份用
%y(两位)还是%Y(四位)必须和输入严格一致,'23'不能配%Y
处理带中文或混合符号的日期字符串要先清洗或改用正则辅助
STR_TO_DATE 不支持正则,也不识别中文字符。遇到 '2023年01月01日' 或 '2023/01/01(星期一)' 这类字符串,直接传入会返回 NULL。
- 优先用
REPLACE()去掉中文:STR_TO_DATE(REPLACE(REPLACE(REPLACE('2023年01月01日', '年', '-'), '月', '-'), '日', ''), '%Y-%m-%d') - 若括号、空格、多余标点多,建议在应用层清洗;MySQL 8.0+ 可用
REGEXP_REPLACE(),但性能明显下降 - 注意:
STR_TO_DATE对时分秒也敏感,'2023-01-01 14:30'必须用'%Y-%m-%d %H:%i',漏掉%H或写成%h(12小时制)会导致失败
NULL 返回值不等于报错,容易被忽略导致逻辑错误
STR_TO_DATE 在格式不匹配时静默返回 NULL,而不是抛异常。这在 WHERE 条件或 INSERT ... SELECT 中极易埋下隐患。
- 调试时加
IS NULL检查:SELECT str, STR_TO_DATE(str, '%Y-%m-%d') AS dt FROM logs WHERE STR_TO_DATE(str, '%Y-%m-%d') IS NULL; - 插入前用
COALESCE(STR_TO_DATE(...), '1970-01-01')提供兜底,但需确认业务是否允许默认值 - 注意时区无关性:该函数输出的是
DATE或DATETIME值,不带时区信息,不会受@@time_zone影响
性能敏感场景下避免在 WHERE 子句中对字段反复调用 STR_TO_DATE
如果表里存的是字符串日期(如 date_str VARCHAR(10)),又经常按日期范围查询,WHERE STR_TO_DATE(date_str, '%Y-%m-%d') BETWEEN '2023-01-01' AND '2023-12-31' 会导致全表扫描——函数作用于列,无法走索引。
- 正确做法是新增生成列并建索引:
ALTER TABLE t ADD COLUMN date_dt DATE AS (STR_TO_DATE(date_str, '%Y-%m-%d')) STORED;,再CREATE INDEX idx_date ON t(date_dt); - 若 MySQL 版本 date_str BETWEEN '2023-01-01' AND '2023-12-31'(前提是字符串格式统一且可字典序比较)
- 注意:
STR_TO_DATE是确定性函数,但 MySQL 旧版本可能不识别其确定性,导致无法在生成列中使用,需确认sql_mode是否含STRICT_TRANS_TABLES等限制











