应优先在导出前用str_to_date+is null清洗非法日期,而非依赖目标库容忍;需显式处理'0000-00-00'等特殊值,并统一格式校验以保障跨库迁移一致性。

直接修复非法日期比跳过或报错更可靠——只要源库还能读,就该在导出前清洗掉无效时间值,而不是靠目标库容忍或迁移工具忽略。
用 STR_TO_DATE + IS NULL 识别非法日期
MySQL 不会主动报错非法日期(比如 '2023-02-30' 或 '0000-00-00'),但 STR_TO_DATE() 在解析失败时返回 NULL,这是最稳定可靠的判断依据。
-
STR_TO_DATE('2023-02-30', '%Y-%m-%d')→NULL,而STR_TO_DATE('2023-02-28', '%Y-%m-%d')→'2023-02-28' - 注意:不能只依赖正则(如
REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'),它能匹配格式但不校验日历逻辑(如 2 月 30 日) - 对
DATETIME字段,需加时间部分:STR_TO_DATE(col, '%Y-%m-%d %H:%i:%s')
批量清洗并替换为 NULL 或默认值
导出前执行清洗 SQL,避免把脏数据带到达梦、Oracle 或其他更严格的数据库中。核心是用 CASE WHEN ... IS NULL THEN ... ELSE ... END 统一处理。
- 安全写法:
SELECT id, IFNULL(STR_TO_DATE(date_col, '%Y-%m-%d'), NULL) AS date_col FROM t1 - 若业务允许,默认填当前日期:
COALESCE(STR_TO_DATE(date_col, '%Y-%m-%d'), CURDATE()) - 多格式兼容(如同时存在
'2023/01/01'和'01-01-2023'):嵌套多个STR_TO_DATE()分支,按优先级排列 - 别忘了
WHERE子句里也得用同样逻辑过滤,否则INSERT ... SELECT可能因类型不匹配失败
达梦/Oracle 迁移前必须绕过 '0000-00-00' 特殊值
MySQL 允许 '0000-00-00'(取决于 sql_mode),但达梦和 Oracle 默认拒绝该值,且不会静默转成 NULL —— 这是迁移报错最集中的点。
- 清洗时显式排除:
WHERE date_col != '0000-00-00' AND date_col IS NOT NULL - 或在
CASE中单独判断:WHEN date_col = '0000-00-00' THEN NULL - 达梦导入要用
TO_DATE(),但传入'0000-00-00'会直接报错,无法兜底 - Oracle 的
TO_DATE('0000-00-00', 'YYYY-MM-DD')同样失败,必须提前剥离
校验脚本里别漏掉隐式转换陷阱
即使清洗过,校验阶段仍可能因字段类型差异导致哈希不一致:比如 MySQL 的 DATETIME 和达梦的 TIME 类型在字符串化时精度不同,或空格填充不一致。
- 校验前统一转成标准字符串:
DATE_FORMAT(COALESCE(STR_TO_DATE(col, '%Y-%m-%d'), '1970-01-01'), '%Y-%m-%d') - 避免直接拼接原始字段值,尤其当目标库自动补零(如
'2023-1-1'→'2023-01-01')而源库没补 - 如果用了
GROUP_CONCAT做整表哈希,记得先ORDER BY id,否则顺序不确定会导致每次哈希结果不同
真正麻烦的不是发现非法日期,而是它混在百万行里只占几条,却让整张表校验失败。清洗动作必须放在导出之前,且要覆盖所有时间字段——哪怕看起来“应该没问题”的字段,也可能藏了 '2024-00-00' 这种肉眼难辨的脏数据。











