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

Laravel导入13万行CSV遇SQL整数格式错误求助

解决PostgreSQL导入空字符串到Nullable Integer字段的报错问题

我完全明白你遇到的困扰——明明数据表所有字段都设为了nullable,但用fast-excel和csv league导入13万行数据时,还是触发了PostgreSQL的类型转换错误。核心原因其实很清晰:

PostgreSQL对于integer类型字段,哪怕允许为null,也不接受空字符串""作为有效值,它只认NULL。而Laravel Excel内部帮你自动把空字符串转换成了null,但fast-excel和csv league没有做这个处理,直接把CSV里的空值(空字符串)传给了数据库,所以才会报la sintaxis de entrada no es válida para integer: «»这个错误。

下面给你几个可行的解决方案,结合你的代码场景优化:

方案1:手动给字段添加空值转换逻辑

你可以写一个简单的辅助函数,批量把空字符串转换成null,然后在导入时调用:

/**
 * 将空字符串转换为null,保留其他有效值
 */
function convertEmptyToNull($value) {
    return $value === '' ? null : $value;
}

修改csv league的导入代码:

$csv = Reader::createFromPath(storage_path('/app/uso_suelo.csv'));
$csv->setHeaderOffset(0);
$records = $csv->getRecords();

foreach ($records as $offset => $record) {
    UsoSuelo::create([
        'cod_pais' => convertEmptyToNull($record['COD_PAIS']),
        'cod_fundo' => convertEmptyToNull($record['COD_FUNDO']),
        'nom_fundo' => convertEmptyToNull($record['NOM_FUNDO']),
        'cod_uso_suelo' => convertEmptyToNull($record['COD_USOSUELO']),
        'dsc_uso_suelo' => convertEmptyToNull($record['DSC_USOSUELO']),
        'cod_rodal' => convertEmptyToNull($record['COD_RODAL']),
        'cod_especie' => convertEmptyToNull($record['COD_ESPECIE']),
        'dsc_especie' => convertEmptyToNull($record['DSC_ESPECIE']),
        'ano_plantacion' => convertEmptyToNull($record['ANO_PLANTACION']),
        'cod_esquema' => convertEmptyToNull($record['COD_ESQUEMA']),
        'dsc_esquema' => convertEmptyToNull($record['DSC_ESQUEMA']),
        'dsc_restriccion_suelo' => convertEmptyToNull($record['DSC_RESTRICCION_SUELO']),
        'sup_ha' => convertEmptyToNull($record['SUP_HA']),
        'cod_z_cpino' => convertEmptyToNull($record['COD_Z_CPINO']),
        'cod_z_prod' => convertEmptyToNull($record['COD_Z_PROD']),
        'cod_sz_prod' => convertEmptyToNull($record['COD_SZ_PROD']),
        'dsc_sz_prod' => convertEmptyToNull($record['DSC_SZ_PROD']),
        'id_area_us' => convertEmptyToNull($record['ID_AREA_US'])
    ]);
}

修改fast-excel的导入代码:

$usoSuelo = (new FastExcel)->import(storage_path('/app/uso_suelo.csv'), function ($line) {
    return UsoSuelo::create([
        'cod_pais' => convertEmptyToNull($line['COD_PAIS']),
        'cod_fundo' => convertEmptyToNull($line['COD_FUNDO']),
        'nom_fundo' => convertEmptyToNull($line['NOM_FUNDO']),
        'cod_uso_suelo' => convertEmptyToNull($line['COD_USOSUELO']),
        'dsc_uso_suelo' => convertEmptyToNull($line['DSC_USOSUELO']),
        'cod_rodal' => convertEmptyToNull($line['COD_RODAL']),
        'cod_especie' => convertEmptyToNull($line['COD_ESPECIE']),
        'dsc_especie' => convertEmptyToNull($line['DSC_ESPECIE']),
        'ano_plantacion' => convertEmptyToNull($line['ANO_PLANTACION']),
        'cod_esquema' => convertEmptyToNull($line['COD_ESQUEMA']),
        'dsc_esquema' => convertEmptyToNull($line['DSC_ESQUEMA']),
        'dsc_restriccion_suelo' => convertEmptyToNull($line['DSC_RESTRICCION_SUELO']),
        'sup_ha' => convertEmptyToNull($line['SUP_HA']),
        'cod_z_cpino' => convertEmptyToNull($line['COD_Z_CPINO']),
        'cod_z_prod' => convertEmptyToNull($line['COD_Z_PROD']),
        'cod_sz_prod' => convertEmptyToNull($line['COD_SZ_PROD']),
        'dsc_sz_prod' => convertEmptyToNull($line['DSC_SZ_PROD']),
        'id_area_us' => convertEmptyToNull($line['ID_AREA_US'])
    ]);
});

方案2:在模型中全局自动处理(更优雅)

你可以在UsoSuelo模型里重写setAttribute方法,实现全局的空字符串转null逻辑,这样所有创建模型的场景都会自动处理:

class UsoSuelo extends Model
{
    // 模型其他代码...

    public function setAttribute($key, $value)
    {
        // 针对所有integer类型字段,自动将空字符串转为null
        $columnType = $this->getConnection()->getDoctrineColumn($this->getTable(), $key)->getType()->getName();
        if ($columnType === 'integer' && $value === '') {
            $value = null;
        }
        parent::setAttribute($key, $value);
    }
}

这样你原有的导入代码不需要任何修改,就能自动解决空字符串的问题。

额外优化:批量插入提升13万行数据的导入速度

循环调用create会产生大量数据库请求,速度极慢,建议改成批量插入(每1000条一批):

以csv league为例:

$csv = Reader::createFromPath(storage_path('/app/uso_suelo.csv'));
$csv->setHeaderOffset(0);
$records = $csv->getRecords();

$batch = [];
$batchSize = 1000; // 可根据服务器性能调整

foreach ($records as $offset => $record) {
    // 先处理空值
    $processedRecord = array_map('convertEmptyToNull', $record);
    // 映射到模型字段
    $batch[] = [
        'cod_pais' => $processedRecord['COD_PAIS'],
        'cod_fundo' => $processedRecord['COD_FUNDO'],
        'nom_fundo' => $processedRecord['NOM_FUNDO'],
        'cod_uso_suelo' => $processedRecord['COD_USOSUELO'],
        'dsc_uso_suelo' => $processedRecord['DSC_USOSUELO'],
        'cod_rodal' => $processedRecord['COD_RODAL'],
        'cod_especie' => $processedRecord['COD_ESPECIE'],
        'dsc_especie' => $processedRecord['DSC_ESPECIE'],
        'ano_plantacion' => $processedRecord['ANO_PLANTACION'],
        'cod_esquema' => $processedRecord['COD_ESQUEMA'],
        'dsc_esquema' => $processedRecord['DSC_ESQUEMA'],
        'dsc_restriccion_suelo' => $processedRecord['DSC_RESTRICCION_SUELO'],
        'sup_ha' => $processedRecord['SUP_HA'],
        'cod_z_cpino' => $processedRecord['COD_Z_CPINO'],
        'cod_z_prod' => $processedRecord['COD_Z_PROD'],
        'cod_sz_prod' => $processedRecord['COD_SZ_PROD'],
        'dsc_sz_prod' => $processedRecord['DSC_SZ_PROD'],
        'id_area_us' => $processedRecord['ID_AREA_US'],
        'created_at' => now(),
        'updated_at' => now()
    ];

    // 每攒够一批就插入
    if (count($batch) >= $batchSize) {
        UsoSuelo::insert($batch);
        $batch = [];
    }
}

// 插入剩余的不足一批的数据
if (!empty($batch)) {
    UsoSuelo::insert($batch);
}

内容的提问来源于stack exchange,提问作者Pamela Rojas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:02:46