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

基于CodeIgniter 3实现Excel/CSV导入PostgreSQL并按program_id去重

解决CodeIgniter 3 + PostgreSQL 13下Excel/CSV导入去重(按program_id过滤)的方案

针对你的场景,核心思路是先获取数据库中已存在的program_id集合,过滤上传数据中的重复项后批量插入,同时结合PostgreSQL的特性做双重保障,适配千级数据量的高效处理。

一、数据库层面的基础保障

先给program_id字段添加唯一约束,从根源上防止重复数据:

ALTER TABLE programs ADD CONSTRAINT unique_program_id UNIQUE (program_id);

这样即使代码过滤出现遗漏,PostgreSQL会直接拦截重复插入操作;如果希望静默忽略重复项,可以用ON CONFLICT DO NOTHING语法(后面代码会提到)。

二、CodeIgniter实现步骤

1. 模型层:获取已有ID + 批量插入

在你的数据模型(比如Program_model.php)中添加两个方法:

class Program_model extends CI_Model {
    // 获取所有已存在的program_id,转成键值对提升查找效率
    public function get_existing_program_ids() {
        $query = $this->db->select('program_id')->get('programs');
        $id_list = array_column($query->result_array(), 'program_id');
        return array_flip($id_list); // 转成键为program_id的数组,O(1)查找
    }

    // 批量插入过滤后的记录,支持PostgreSQL的冲突忽略
    public function batch_insert_programs($data) {
        if (empty($data)) return 0;

        // 方式1:用CI原生insert_batch(配合数据库唯一约束,重复会报错)
        $this->db->insert_batch('programs', $data);

        // 方式2:原生SQL+ON CONFLICT,静默忽略重复项(推荐高并发场景)
        // $columns = implode(', ', array_keys($data[0]));
        // $value_strings = [];
        // foreach ($data as $row) {
        //     $escaped_vals = array_map(fn($val) => $this->db->escape($val), $row);
        //     $value_strings[] = '(' . implode(', ', $escaped_vals) . ')';
        // }
        // $sql = "INSERT INTO programs ($columns) VALUES " . implode(', ', $value_strings) . " ON CONFLICT (program_id) DO NOTHING";
        // $this->db->query($sql);

        return $this->db->affected_rows();
    }
}

2. 控制器层:处理上传 + 过滤数据

以PhpSpreadsheet处理Excel为例(CSV可以用更轻量的fgetcsv),在控制器中实现导入逻辑:

class Import_controller extends CI_Controller {
    public function __construct() {
        parent::__construct();
        $this->load->model('Program_model');
        // 加载PhpSpreadsheet(需提前通过Composer或手动放入third_party)
        require_once APPPATH . 'third_party/PhpSpreadsheet/src/PhpSpreadsheet/IOFactory.php';
    }

    public function import() {
        // 校验上传文件
        if ($_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
            echo "文件上传失败,请检查后重试";
            return;
        }

        // 读取Excel文件
        $file_path = $_FILES['import_file']['tmp_name'];
        $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_path);
        $worksheet = $spreadsheet->getActiveSheet();
        $highest_row = $worksheet->getHighestRow();

        // 获取已存在的program_id集合
        $existing_ids = $this->Program_model->get_existing_program_ids();
        $insert_data = [];

        // 遍历数据行(跳过表头,假设第一行是表头)
        for ($row = 2; $row <= $highest_row; $row++) {
            $program_id = trim($worksheet->getCell('A' . $row)->getValue());
            // 读取其他字段(根据你的表结构调整列位置)
            $name = trim($worksheet->getCell('B' . $row)->getValue());
            $description = trim($worksheet->getCell('C' . $row)->getValue());

            // 过滤已存在的记录
            if (!isset($existing_ids[$program_id])) {
                $insert_data[] = [
                    'program_id' => $program_id,
                    'name' => $name,
                    'description' => $description
                    // 注意:不要包含自增id字段,数据库会自动生成
                ];
            }
        }

        // 执行批量插入
        if (!empty($insert_data)) {
            $count = $this->Program_model->batch_insert_programs($insert_data);
            echo "成功插入 {$count} 条新记录";
        } else {
            echo "没有新记录需要插入";
        }
    }
}

3. CSV文件处理优化(可选)

如果是CSV文件,用fgetcsv比PhpSpreadsheet更高效,适合大文件:

// 替换控制器中的文件读取部分
$file = fopen($file_path, 'r');
fgetcsv($file); // 跳过表头
while (($row = fgetcsv($file)) !== false) {
    $program_id = trim($row[0]);
    $name = trim($row[1]);
    $description = trim($row[2]);

    if (!isset($existing_ids[$program_id])) {
        $insert_data[] = [
            'program_id' => $program_id,
            'name' => $name,
            'description' => $description
        ];
    }
}
fclose($file);

三、千级数据量适配要点

  • 查找效率:用array_flip将已存在的program_id转成键值对,查找时间复杂度从O(n)降到O(1),处理千级数据毫无压力。
  • 批量插入:insert_batch或原生批量SQL减少数据库交互次数,比单条插入效率提升数倍。
  • 内存控制:如果是超大文件,建议分块读取处理,避免一次性加载所有数据到内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 04:45:42