☰
MySQL字符串日期转换全攻略:从函数选型到索引优化
2026/10/20 14:30:04 网站建设 项目流程

开头先说个实际现象:不管你是做业务开发、数据维护还是搞报表分析,只要在MySQL里待过一阵子,基本都会碰上“字符串日期”这个玩意儿。我最早遇到这个问题,是在接手一个老系统的订单表,里面的下单时间字段居然是varchar类型,存的格式五花八门,有“2023-10-15 14:30:00”这种标准的,也有“20231015”这种纯数字,甚至还有“2023/10/15”这种带斜杠的。那时候我才意识到,字符串日期格式转换不是“懂个函数就行”的小事,它牵扯到查询性能、数据清洗、索引设计,甚至线上故障。

这篇文章我想把这几年处理字符串日期转换的经验一次性讲清楚,从函数选型到格式串编写,从隐式转换陷阱到慢查询优化,再到实际数据清洗的完整流程,适合刚接触MySQL的新手,也适合想系统梳理这块知识的老手。

1. 为什么字符串日期转换是绕不开的坎

1.1 字符串日期到底从哪来

字符串日期绝大多数是历史包袱。早期系统设计的时候,为了省事,直接把前端传过来的日期字符串塞进varchar字段。也有一些是导入外部数据源时留下的,比如从Excel、CSV导入,或者是接口对接时收到对方给的字符串格式时间戳。这种设计在业务初期问题不明显,一旦数据量上来、查询复杂起来,麻烦就一个接一个。

举个例子,你在varchar字段上查某天的数据:

SELECT * FROM orders WHERE order_date = '2023-10-15';

数据量小的时候感觉不到问题,等到几百万行的时候,这个查询极有可能走全表扫描,因为MySQL对varchar字段和字符串常量比较时,如果字段是varchar,那这条语句还勉强能用上索引;但如果字段存储的是“20231015”这种格式,你拿“2023-10-15”去匹配,那神仙也救不了你。

还有一类常见来源是日志表。程序把时间戳以字符串形式写进日志表,方便直接读,但后面要做时间范围统计的时候就傻眼了,因为字符串比较和日期比较完全是两码事。字符串比较是按字典序来的,'2023-10-9' 会排在 '2023-10-15' 前面,而日期比较是按时间先后来的,这俩结果可能完全相反。

1.2 先把核心函数认全

MySQL里做字符串日期转换,核心就两个函数:STR_TO_DATE和DATE_FORMAT。前者把字符串变成日期类型,后者把日期类型变成指定格式的字符串。这俩是一对,配合起来几乎能处理所有常规需求。

-- 字符串转日期 SELECT STR_TO_DATE('2023-10-15', '%Y-%m-%d'); -- 结果:2023-10-15 -- 日期转字符串 SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); -- 结果:2023-12-01 14:30:00

除了这俩,还有几个函数也常被问到。CAST('2023-10-15' AS DATE)可以转标准格式的字符串,但容错能力比STR_TO_DATE差很多。UNIX_TIMESTAMP('2023-10-15')能把字符串日期转成时间戳,但它要求字符串是相对标准的格式,稍微乱一点就返回0。DATE()函数则是从日期时间表达式里提取日期部分,不涉及格式解析。

所以我的建议是:凡是需要“解析”字符串的场景,优先用STR_TO_DATE,它的格式串机制非常灵活,可以应付“2023/10/15”“20231015”“15-10-2023”各种非主流格式。凡是需要“格式化输出”的场景,用DATE_FORMAT。其他函数作为补充,别把它们当主力。

1.3 何时需要真正转换:存储层转换 vs 查询层转换

很多人一上来就问:“我该不该把这个varchar字段改成date类型?”这个问题其实要分情况。如果这个字段以后要频繁参与范围查询、排序、分组,那我建议直接在存储层做转换,也就是把字段类型改成DATE或DATETIME,一劳永逸。如果只是偶尔需要按日期筛选,就不必动表结构,在查询层用STR_TO_DATE转一下就行。

存储层转换的代价是:改表结构、清洗历史数据、验证业务逻辑,工作量大,但收益也大。查询层转换的代价是:每次查询都要做一次函数运算,如果字段上没有索引,性能提升有限;如果有索引,对字段套函数会让索引失效。

怎么权衡?我个人的判断标准是:看这个字段是否充当“核心筛选维度”。如果业务里天天要按时间汇总、对账、拉报表,那就值得彻底改造。如果只是偶尔用一下,比如后台管理页面的某个筛选条件,那就别折腾,查询时转一下就行。后面第3节我会详细讲存储层转换的完整流程。

2. 核心细节与格式串编写要点

2.1 格式串语言的正确打开方式

STR_TO_DATE的核心是格式串,格式串里的每个占位符代表一种日期组成部分。我把最常用的几个列出来,并用一个表格说明:

格式符含义举例
%Y四位数年份2023
%y两位数年份23
%m两位数月份10
%c月份(1-12,不补零)10
%d两位数日期15
%e日期(1-31,不补零)15
%H24小时制小时14
%h12小时制小时02
%i分钟30
%s秒00
%pAM/PMPM

这里有个容易踩的坑:很多新手把分钟写成%M,结果%M在MySQL里代表的是月份名称(January、February这种),直接返回NULL。我第一次用的时候也栽在这上面,排查了好久才发现是%i和%M的问题。

格式串必须和输入字符串严格对应,多一个字符、少一个字符都会导致解析失败。比如:

-- 正确 SELECT STR_TO_DATE('2023-10-15 14:30:00', '%Y-%m-%d %H:%i:%s'); -- 错误:格式串里秒的部分没写 SELECT STR_TO_DATE('2023-10-15 14:30:00', '%Y-%m-%d %H:%i'); -- 结果:2023-10-15 14:30:00(注意,秒被截掉了,但不会报错) -- 错误:输入有多余空格 SELECT STR_TO_DATE('2023-10-15 ', '%Y-%m-%d'); -- 结果:NULL

第二条的截断行为其实是MySQL的容错机制,它不报错,但秒没了。这种“静默截断”在数据清洗的时候很危险,你以为是全量转换成功,实际上有部分数据被悄悄砍了一截。

2.2 分隔符和混杂格式的处理

日期字符串的分隔符花样百出,有横杠的、斜杠的、点的、空格的,还有纯数字的。STR_TO_DATE的好处是格式串里可以直接写分隔符,只要和输入串对应上就行。

-- 斜杠分隔 SELECT STR_TO_DATE('2023/10/15', '%Y/%m/%d'); -- 点分隔 SELECT STR_TO_DATE('2023.10.15', '%Y.%m.%d'); -- 纯数字 SELECT STR_TO_DATE('20231015', '%Y%m%d');

但有一类情况比较麻烦:同一个字段里混了多种格式。比如某张表的历史数据里,有的存“2023-10-15”,有的存“2023/10/15”,还有的存“20231015”。这种情况下,不能指望一条STR_TO_DATE搞定,得先做数据分布摸底,看看一共有多少种格式,然后分类处理。

我的做法是写一条查询,把所有可能的格式串都跑一遍,看哪些能转换、哪些返回NULL,把NULL的那部分再单独拎出来看长什么样:

SELECT order_date, CASE WHEN order_date REGEXP '^[0-9]{8}$' THEN 'yyyyMMdd' WHEN order_date REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}' THEN 'yyyy-MM-dd' WHEN order_date REGEXP '^[0-9]{4}/[0-9]{2}/[0-9]{2}' THEN 'yyyy/MM/dd' ELSE 'unknown' END AS fmt_type FROM orders GROUP BY fmt_type;

这种摸底查询非常值得花时间做,它决定了后续清洗方案的复杂度。如果只有一种格式,一条UPDATE就完了,如果混了三种,就得写多条UPDATE,甚至要上临时表。

2.3 时区、隐式转换和“NULL陷阱”

字符串日期转换还有一个容易被忽略的细节:时区。STR_TO_DATE返回的是MySQL会话时区下的时间。如果你的数据库时区是UTC,而业务数据是北京时间,那转换结果在做日期比较时需要小心。不过大多数场景下,大家存的是“不带时区信息的本地时间字符串”,这种情况转成DATE或DATETIME不会有偏差,因为MySQL不把它当UTC时间处理。

真正需要留意的是CAST('2023-10-15 14:30:00' AS DATETIME)这类操作。DATEDIFF、TIMESTAMPDIFF等函数在处理带时间部分的字符串时,会隐式调用类型转换。如果字符串格式不符合MySQL的默认日期格式YYYY-MM-DD,转换就会失败,结果可能是NULL或者0。

还有一种“NULL陷阱”特别值得警惕。STR_TO_DATE在解析失败时会返回NULL,而不是报错。这意味着:

UPDATE orders SET order_date_new = STR_TO_DATE(order_date, '%Y-%m-%d');

执行完之后,你发现没有报错,以为全部转换成功了。但一查order_date_new IS NULL的记录,可能有几百条,都是因为原数据格式不符合%Y-%m-%d。这种静默失败是数据清洗里最容易出现的问题,所以任何转换操作之后,一定要立刻做一轮校验,确认非NULL记录数和总记录数一致。

顺便说下MySQL的严格模式。如果你的表字段是DATE类型,插入一个非法日期比如'2023-02-30',在严格模式下会直接报错,非严格模式下会被置为0000-00-00。这也会产生“看起来成功,实际上数据是脏的”的情况。建议在数据清洗期间,把sql_mode临时调成严格模式,尽早暴露问题。

2.4 边界情况:闰年、二月、零值和空字符串

边界情况是字符串日期转换最容易翻车的地方,但平时很少被提到。

闰年问题:STR_TO_DATE('2023-02-29', '%Y-%m-%d')会返回NULL,因为2023年不是闰年。如果数据里有这种脏值,你需要在清洗前决定好策略:是丢弃、置空,还是修正为2023-02-28?不同业务策略不同,但一定要有策略,不能放任不管。

零值问题:有些系统会把日期存成'0000-00-00',这串字符转DATE类型时会有麻烦。在严格模式下直接报错,非严格模式下变成0000-00-00,还是个DATE类型。查询日期范围时,WHERE date_col BETWEEN '2023-01-01' AND '2023-12-31'不会包含0000-00-00,但这玩意儿会在统计时占用一个NULL分组,容易引起数据对不上。

空字符串问题:varchar字段里,''和NULL是两回事。STR_TO_DATE('', '%Y-%m-%d')返回NULL,但空字符串本身在很多统计函数里不会被计数,而NULL也不会。如果业务需要区分“没填写日期”和“填了非法日期”,得在清洗前把原始值保留备份,方便追溯。

3. 实操过程:从脏乱字符串到干净的日期字段

3.1 摸底:先看一眼你的字符串都长什么样

开始改表之前,先花半小时做摸底,这个步骤能省下后面几天的调试功夫。我用的是最笨也最有效的办法:抽样 + 分组统计。

假设有一张记录表event_log,里面有个字段event_time是varchar(20)。先抽样看几行:

SELECT event_time FROM event_log LIMIT 20;

接着统计长度分布:

SELECT LENGTH(event_time) AS len, COUNT(*) AS cnt FROM event_log GROUP BY LENGTH(event_time);

长度分布能很快告诉你数据是否统一。如果长度集中在8位、10位、19位,那基本可以判断有对应三种格式。然后按长度抽样看具体内容,用正则或者CASE WHEN归一下类。

这里我补充一个技巧:如果数据量很大,几千万行,抽样就用TABLESAMPLE或者WHERE id % 1000 = 0这种能用上主键条件的采样方式,别直接用ORDER BY RAND(),那个在大表上是灾难,会触发全表扫描加排序,卡半天。

3.2 分步转换:用SELECT验证再写UPDATE

数据清洗的铁律:先在SELECT里转换并验证,确认无误后再写UPDATE。不要一上来就UPDATE,因为一旦写错,虽然可以回滚,但大表回滚非常痛苦。

正确步骤是:

第一步,SELECT转换,看结果:

SELECT event_time, STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') AS converted FROM event_log WHERE LENGTH(event_time) = 19 LIMIT 50;

确认转换结果没错,没有NULL。

第二步,统计转换失败率:

SELECT COUNT(*) AS total, SUM(CASE WHEN STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') IS NULL THEN 1 ELSE 0 END) AS failed FROM event_log WHERE LENGTH(event_time) = 19;

第三步,检查失败样本长什么样:

SELECT event_time FROM event_log WHERE LENGTH(event_time) = 19 AND STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') IS NULL LIMIT 50;

看到失败样本后,再针对它们写单独的清洗规则。比如有些是里面混了T字母,2023-10-15T14:30:00,Java的ISO时间格式常见,这时候格式串改成%Y-%m-%dT%H:%i:%s就行。

确认所有规则都覆盖后,才写UPDATE。UPDATE的时候,我习惯用分批策略,别一次UPDATE全表。加个WHERE id BETWEEN ? AND ?的条件,一次处理几万行,既能控制风险,也能避免长时间锁表。

3.3 改表结构与索引优化

数据清洗完成、新字段的值都正确之后,才轮到改表结构。我的顺序是:先加新列写转换值,再校验,最后删旧列改名。这套流程看着绕,但最安全。

加新列:

ALTER TABLE event_log ADD COLUMN event_time_new DATETIME NULL;

回填数据:

UPDATE event_log SET event_time_new = STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s');

校验:

SELECT COUNT(*) FROM event_log WHERE event_time_new IS NULL;

这一步必须保证结果是0。如果有NULL,先处理,别往下走。

确认无误后,升级索引、删旧列:

ALTER TABLE event_log DROP INDEX idx_event_time, DROP COLUMN event_time, CHANGE COLUMN event_time_new event_time DATETIME NOT NULL, ADD INDEX idx_event_time (event_time);

有一点要注意:CHANGE COLUMN的时候如果字段原来有NULL值,改成NOT NULL会失败,所以清洗阶段得把NULL处理干净。这块我之前吃过亏,最后发现有一批空字符串被转成了NULL,导致ALTER直接报错,又倒回去补数据。

索引优化这块多说两句。varchar字段上的索引和date/datetime字段上的索引,对范围查询的优化效果差距非常大。日期类型字段占用空间小,B+树索引更紧凑,范围扫描效率高。你拿varchar字段做BETWEEN,MySQL要么全表扫,要么做隐式转换后索引失效。改成真正的DATETIME字段之后,同样的SQL,执行计划可能从全表扫描变成索引范围扫描,查询耗时能差出十倍以上。

3.4 增量数据怎么防再脏

历史数据清洗干净只是第一步,如果不堵住入口,过两个月又会被脏数据污染。这个入口有两条:一条是业务代码写入路径,一条是外部数据导入路径。

业务代码写入路径:如果原来的INSERT语句是直接拼字符串塞进去的,要改成用参数化方式传DATETIME类型,或者在应用层统一格式化后再写入。最理想的方案是:数据库字段改成DATETIME之后,ORM框架会自动把程序里的日期对象映射为日期类型,写进去的就是标准格式。

外部数据导入路径:ETL或者手工导入Excel的时候,要加一个校验步骤,导入前先跑一遍格式检查,不合法的直接拒绝入库,或者进到待清洗临时表。这个校验逻辑和前面说的摸底查询一样,用STR_TO_DATE转一次,IS NULL的就拒掉。

我自己还习惯在业务表上建一个INSERT触发器做兜底校验,一旦发现新插入的日期字段是NULL或者非法格式,直接报错。这个手段虽然有点激进,但对那些数据质量要求严格的场景非常有效。

4. 查询效率:为什么一个日期条件让慢查询暴增

4.1 隐式转换让索引失效的典型场景

字符串日期转换不只是数据清洗的问题,你会发现很多生产环境的慢查询也跟它有关。最典型的场景是:字段类型是varchar,索引也在varchar字段上,但你拿一个日期类型的参数去和它比较。

-- event_time 是 varchar 类型 SELECT * FROM event_log WHERE event_time >= '2023-10-15 00:00:00' AND event_time <= '2023-10-16 00:00:00';

MySQL的隐式类型转换规则是:如果比较的双方一个是字符串,一个是日期/时间类型,MySQL会把字符串转成日期,而不是把日期转成字符串。所以上面这条SQL实际执行的时候,会把event_time字段的每一行的值都调用一次字符串转日期的函数,索引自然就废了。

怎么确认是不是这个问题?看执行计划里Extra列有没有Using where,以及type是不是ALL或者index。如果明明有索引却走了全表扫描,大概率就是隐式转换。

规避办法有三个:一是彻底改字段类型,这是治本方案;二是查询时明确把字符串转成日期类型,比如:

SELECT * FROM event_log WHERE STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') >= '2023-10-15';

这种写法也有问题,函数放在字段上,还是会导致索引失效。真正靠谱的临时方案是:让传入参数和字段类型保持一致。既然字段是varchar,你就传字符串进去,但前提是字符串格式排序和日期排序一致。这也就是为什么我强烈建议大家把日期字符串统一成YYYY-MM-DD HH:MM:SS格式,因为这种格式的字典序等于时间顺序,字符串比较的结果和日期比较的结果一致。这种情况下,varchar字段上的字符串比较依然可以用索引,也算是一个折中方案。

但如果字段里存的是YYYY/MM/DD或者YYYYMMDD,那字符串比较的结果和日期比较就完全对不上了,这就是很多“明明索引好好的,一加日期范围就慢”的深层原因。

4.2 函数索引和生成列的正确用法

如果因为历史原因没法改字段类型,但又要频繁按日期查询,MySQL 8.0+ 提供了函数索引,可以直接在STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s')上建索引。我先说明,这个操作只能在MySQL 8.0以上版本使用,如果你还在用5.7或者更早版本,官方不支持直接建函数索引,那你只能走生成列这条路。

函数索引的正确写法:

CREATE INDEX idx_event_time_convert ON event_log ((STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s')));

查询的时候,只要SQL里用了和索引定义完全一致的表达式,优化器就可能走这个索引:

SELECT * FROM event_log WHERE STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') >= '2023-10-15';

生成列(Generated Column)的思路类似,加一列虚拟列,值由基列计算出来,然后在虚拟列上建索引:

ALTER TABLE event_log ADD COLUMN event_time_date DATETIME GENERATED ALWAYS AS (STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s')) STORED; CREATE INDEX idx_event_time_date ON event_log (event_time_date);

生成列用STORED还是VIRTUAL?我的经验是,如果查询频繁且表是读多写少,用STORED,物理存储列,索引读取快,但会增加存储和写入开销。如果表写多读少,用VIRTUAL,不落盘,索引也能用,但查询时可能要计算。大多数报表场景,我会选STORED,因为写入频率远低于查询频率,多耗一点磁盘完全值得。

函数索引和生成列是“不改主表类型”前提下的最优解。但要注意:如果你把原有varchar字段彻底改成DATETIME,那就不需要这些了,直接普通索引就够用。所以函数索引适合“不能改表结构,但想提升查询性能”的中间态场景。

4.3 批量更新时的性能注意

数据清洗时的大规模UPDATE,很多人踩过锁的坑。几百万行一次UPDATE,虽然InnoDB是行锁,但你有WHERE条件,扫描过程中会持有大量行锁,加上binlog复制延迟,主库压力巨大,搞不好还影响线上业务。

我的经验是分批处理,按照主键范围切段,每段几万行:

-- 每次执行一段,循环处理 UPDATE event_log SET event_time_new = STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') WHERE id BETWEEN 1 AND 50000;

等这一批执行完,再跑下一批。可以用存储过程写个循环,也可以在外面用脚本控制,本质上都是“小步快跑”。

还有一点:UPDATE之前先把旧字段上的索引删掉。不然每次UPDATE还没值可以写入,索引维护的额外开销会拖慢速度。等全部清洗完、新列数据校验无误之后,再一次性地把索引建回来。这个优化对大数据量特别明显,我试过同样的几百万行清洗,带索引跑要二十多分钟,去掉索引再跑,十分钟出头就能完成。

另外一个容易被忽视的点:开启事务还是关闭?如果数据量很大,建议分批UPDATE,每批提交一次,避免单个事务积累过多undo log。经验值是一批5万行左右,超过这个规模,回滚段和临时文件都会明显膨胀。

5. 常见问题与排查技巧实录

5.1 常见报错速查表

字符串日期转换的报错花样其实不多,但每个都很有迷惑性,我整理了一个速查表,方便大家对照排查。

现象可能原因解决办法
返回NULL,不报错输入字符串和格式串不匹配检查分隔符、位数、大小写,尤其是%i和%M
Incorrect datetime value: '2023-10-15'严格模式下,字符串转日期类型失败检查字符串是否符合目标类型范围,尤其注意零值日期
Truncated incorrect DATE value字符串里有残余字符,解析到一半失败用LENGTH看字符串实际长度,检查隐藏字符、T字母
Data too long for columnDATE_FORMAT生成的字符串超出目标列长度检查目标列varchar长度是否够
转换结果日期对不上12小时制和24小时制混用%H是24小时制,%h是12小时制,看输入串有没有AM/PM
闰年日期变成NULL输入存在2月30日这类非法日期先在数据分布里把这些找出来,单独处理
索引没生效隐式转换导致字段上套了函数改成函数索引或者生成列,彻底改字段类型
月份变成00输入串里月份位置缺失或非数字检查原字符串是否真的有月份部分

5.2 排查思路:先把问题定位在“格式串”还是“数据本身”

遇到转换结果不对,我的排查顺序是固定的。先看单条样本,拿一条有问题的数据单独执行STR_TO_DATE试格式串,一点点加字符,哪一步加完变成NULL,问题就出在哪一步。这个方法虽然慢,但定位问题非常准。

比如'2023-10-15 14:30:00'转不出来,我会这样测:

SELECT STR_TO_DATE('2023-10-15', '%Y-%m-%d'); SELECT STR_TO_DATE('2023-10-15 14', '%Y-%m-%d %H'); SELECT STR_TO_DATE('2023-10-15 14:30', '%Y-%m-%d %H:%i'); SELECT STR_TO_DATE('2023-10-15 14:30:00', '%Y-%m-%d %H:%i:%s');

哪一条返回NULL,就把注意力放在那一个位置。这个方法对排查带空格、带T字母、带毫秒的字符串特别有效。

然后看数据集整体。用前面说的分组统计,按长度、格式类型分组,找出所有异常样本集。这一步的主要目的是区分“少数脏数据”和“普遍格式问题”。如果是少数脏数据,写针对性的清洗规则即可;如果是普遍格式问题,那就得重新审视数据来源,考虑是不是上游本来就传的非标准格式。

5.3 排查工具:用视图当试金石

我后面发现一个特别好用的排查方式:把转换逻辑写进一个视图里,查询的时候直接看视图,数据库会自动进行转换和解析。这样做的好处是,业务查询不用改,只改视图定义里的STR_TO_DATE格式串就行。对于临时筛选、数据看板这类场景,这个方案能省掉反复改SQL的麻烦。

CREATE VIEW v_event_log AS SELECT id, STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') AS event_time_converted, event_detail FROM event_log;

然后业务查询写WHERE event_time_converted >= '2023-10-15'。视图是个逻辑层,不影响底层表结构,改起来很方便,适合过渡期使用。但是要注意:视图查询性能不一定比直接查表好,尤其是转换表达式写在视图里,没法直接套用底层索引。所以这个方案只适合临时救急,长期方案还是得改物理字段类型。

5.4 我个人的几个习惯

最后说几个我这些年养成的习惯,不一定适合所有团队,但至少帮我少踩了很多坑。

第一,任何日期写入MySQL之前,都先在应用层统一转成YYYY-MM-DD HH:MM:SS这个24小时制格式。它既能按字典序比较,又能直接通过STR_TO_DATE解析,还能被CAST识别,是兼容性最好的格式。对比一下,YYYY/MM/DD虽然在STR_TO_DATE里也能解析,但很多第三方工具、Excel、BI软件不认这个格式。

第二,定义表结构的时候,日期字段优先用DATETIME。除非你确实只需要日期部分且不关心时间,否则别用DATE类型存时间,也别用varchar存日期。TIMESTAMP虽然有占用空间小的优势,但2038年问题、时区转换问题都让它不如DATETIME省心。

第三,每次写STR_TO_DATE或者DATE_FORMAT,我都把格式串写在SQL注释里,方便后来人看懂。比如:

-- 解析格式:YYYY-MM-DD HH:MM:SS SELECT STR_TO_DATE(event_time, '%Y-%m-%d %H:%i:%s') FROM event_log;

这个习惯在团队协作里特别重要,因为格式串读起来实在不够直观,%Y%m%d和%y%m%d一眼根本分不清差在哪,注释能避免很多误会。

第四,也是最重要的一条:转换之后必须马上做校验。我见过太多人跑完UPDATE就直接下班,第二天发现数据对不上,只能从备份回滚。校验SQL也很简单:

SELECT COUNT(*) FROM event_log WHERE event_time_new IS NULL AND event_time IS NOT NULL;

保证这个结果是0,才算真正完成。数据清洗这件事,慢一点没关系,漏了才是大麻烦。

字符串日期转换说到底不难,函数就那么几个,格式串也就几十个符号。真正考验人的是边界情况、脏数据处理、索引失效这些实操细节。希望这篇把我知道的都写清楚了,你们在项目里遇到类似问题,至少能少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询