You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LOAD DATA LOCAL INFILE因日期字段异常无法正常导入CSV数据

问题成因
  • 错误捕获配置失效:你当前PDO设置的错误级别是PDO::ERRMODE_WARNING,这个级别下LOAD DATA触发的数据库报错不会被try/catch捕获,只会生成不中断流程的PHP级警告,最终表现就是没有数据插入、也没有可见错误提示。
  • MySQL严格模式阻断容错逻辑:MySQL 5.7及以上版本默认开启STRICT_TRANS_TABLES严格模式,STR_TO_DATE()接收到不符合格式、不存在的非法日期值时,会直接终止整个SQL执行,根本不会走到你写的IF判断分支,所以之前写的容错代码完全没机会运行。
  • 测试场景和实际问题不匹配:你用带空值的测试集验证能通过,是因为空字符串传入STR_TO_DATE会直接返回NULL,不会触发严格模式报错;但实际CSV里的非法值通常是格式错误(比如月/日顺序写反、带斜杠以外的分隔符)、不存在的日期(比如2月31日)、字段前后带不可见空格/特殊字符,这类值才是触发报错中断的根因。
可行解决方法

方案1:导入时临时调整会话SQL模式(大容量导入场景最快生效)

在执行LOAD DATA前,先临时修改当前数据库连接的SQL模式,关闭严格校验、去掉零日期限制,让STR_TO_DATE遇到非法值时返回NULL而非直接报错,同时把PDO错误模式调整为异常模式方便排查问题。
修改后的可运行代码如下:

$conn = new \PDO("mysql:host=host_name;dbname=db_name;", "username", "password", array(
    PDO::MYSQL_ATTR_LOCAL_INFILE => true,
    // 调试和正式导入都建议开异常模式,所有错误会直接抛出可被捕获
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
));
// 仅对当前连接生效,不会修改数据库全局配置
$conn->exec("SET SESSION sql_mode = ''");
$sql = "LOAD DATA LOCAL INFILE 'services.csv' REPLACE INTO TABLE service_ready_list 
        FIELDS TERMINATED BY ',' 
        OPTIONALLY ENCLOSED BY '\"' 
        LINES TERMINATED BY '\\n' 
        IGNORE 1 ROWS 
        (@col1,@col2,@col3) 
        SET 
            service_number=@col1, 
            service_name=@col2,
            // 加TRIM去掉字段前后可能存在的不可见空格、换行,避免解析失败
            // 非法日期统一存NULL,兼容性比0000-00-00更好,不会触发高版本MySQL限制
            ready_date = NULLIF(STR_TO_DATE(TRIM(@col3), '%d/%m/%Y'), '0000-00-00')
";
try {
    $conn->prepare($sql)->execute();
    echo "File import completed....\n";
} catch (Exception $ex) {
    echo $ex->getMessage();
    echo "\n Error occured\n";
}

方案2:预清洗CSV数据(数据准确性最高)

如果不想调整数据库配置,可在导入前先做数据校验:

  • 逐行读取CSV第三列的日期值,先trim清除前后空白字符
  • 用PHP的DateTime::createFromFormat('d/m/Y', $dateVal)逐行校验日期格式和有效性
  • 把非法值统一替换为NULL或业务指定的默认值,生成清洗后的临时CSV文件再执行LOAD DATA导入
    这个方案适合对数据一致性要求高的场景,不会把非法日期静默转为空值漏掉数据问题。

方案3:优化日期转换容错逻辑

如果已经关闭了严格模式,可以把日期处理逻辑替换为更稳妥的CASE判断,覆盖更多边界场景:

ready_date = CASE 
    WHEN TRIM(@col3) = '' THEN NULL
    WHEN STR_TO_DATE(TRIM(@col3), '%d/%m/%Y') IS NOT NULL THEN STR_TO_DATE(TRIM(@col3), '%d/%m/%Y')
    ELSE NULL
END

注意:MySQL 8.0+对0000-00-00零日期的限制更严格,哪怕关闭严格模式如果没移除NO_ZERO_DATE规则也会报错,因此非法日期统一存储NULL是兼容性最好的选择。


内容的提问来源于stack exchange,提问作者Gayan Fernando

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:57:06