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

使用PHPExcel导出PHP数据至Excel速度极慢,求排查优化

回答:

核心性能瓶颈分析与优化方案

你的代码里有两个致命的性能问题,这也是导致3000条记录导出耗时超30分钟的主要原因,下面逐个解决:

1. 彻底解决N+1数据库查询问题

你在循环中每条记录都调用$data_pic = $this->App_m->detilpic_m($row_aplikasi->idpic),这意味着3000条记录会触发3000次独立的数据库查询——数据库的连接、查询、结果返回开销会被放大到极致,这是最影响速度的点。

优化步骤:

  • 先批量获取所有需要的pic数据,存入数组做映射,循环时直接从数组取值,避免重复查询。

修改控制器代码:

function download_aplikasi() { 
    $this->Weblog_m->save_log_m("Download aplikasi"); 
    $data_aplikasi = $this->Io_excel_m->get_aplikasi_m(); 

    // --- 新增:批量获取pic数据并构建映射 ---
    $idpic_list = array_column($data_aplikasi, 'idpic');
    $idpic_list = array_unique(array_filter($idpic_list)); // 去重并过滤空值
    $pic_data = $this->App_m->get_batch_pic_m($idpic_list);
    $pic_map = [];
    foreach($pic_data as $pic){
        $pic_map[$pic->idpic] = $pic;
    }
    // --- 批量获取结束 ---

    // Create new PHPExcel object 
    $objPHPExcel = new PHPExcel(); 
    $objPHPExcel->getActiveSheet()->getStyle('1:1')->getFont()->setBold(true); 

    // ... 表头设置部分保持不变 ...

    $i = 1; 
    if (count($data_aplikasi) > 0) { 
        foreach ($data_aplikasi as $row_aplikasi) { 
            // --- 替换原来的单条查询 ---
            $data_pic = isset($pic_map[$row_aplikasi->idpic]) ? $pic_map[$row_aplikasi->idpic] : (object)['nama_user' => '', 'nomorhp' => ''];
            // --- 替换结束 ---
            $i = $i + 1; 
            // ... 单元格赋值部分保持不变 ...
        } 
    } else {
        // ... 无数据处理部分保持不变 ...
    } 

    // ... 后续输出部分保持不变 ...
} 

在模型中新增批量查询方法:

function get_batch_pic_m($idpic_list){
    if(empty($idpic_list)) return [];
    $this->db->where_in('idpic', $idpic_list);
    // 假设你的pic表是tbl_pic,根据实际表名调整
    return $this->db->get('tbl_pic')->result(); 
}

2. 优化PHPExcel单元格写入效率

你现在用链式setCellValue逐个设置单元格,3000条×29列=87000次调用,每次调用都会触发PHPExcel的内部状态更新和校验,开销极大。换成批量写入的方式,速度能提升数倍。

优化步骤:

  • 构建二维数组存储所有数据,用fromArray方法一次性写入,减少PHPExcel的内部操作次数。

修改控制器代码(表头+数据写入部分):

// Create new PHPExcel object 
$objPHPExcel = new PHPExcel(); 

// --- 批量设置表头 ---
$header = [
    'TGL_APLIKASI', 'CD_CABANG', 'NAMA_CABANG', 'NIP', 'NAMA_PEGAWAI',
    'JABATAN', 'NAMA_NASABAH', 'NO_REK', 'NO_CIF', 'DOB_NASABAH',
    'JNS_NASABAH', 'NOMINAL', 'ID_NASABAH', 'PRODUK', 'JNS_PINJAMAN',
    'JNS_USAHA', 'LAMA_USAHA', 'JNS_JAMINAN', 'PIC_NASABAH', 'TELP_NASABAH',
    'NAMA_TOKO', 'KATEGORI', 'CATATAN', 'KETERANGAN', 'PIC_UNIT',
    'TELP_PIC', 'KAT_PRODUK', 'PROGRESS', 'PROGRESS_NOTE'
];
$objPHPExcel->getActiveSheet()->fromArray([$header], null, 'A1');
$objPHPExcel->getActiveSheet()->getStyle('1:1')->getFont()->setBold(true);
// --- 表头设置结束 ---

// --- 批量构建数据数组 ---
$excel_data = [];
if (count($data_aplikasi) > 0) {
    foreach ($data_aplikasi as $row_aplikasi) {
        $data_pic = isset($pic_map[$row_aplikasi->idpic]) ? $pic_map[$row_aplikasi->idpic] : (object)['nama_user' => '', 'nomorhp' => ''];
        $excel_data[] = [
            date("d/m/Y", strtotime($row_aplikasi->tglaplikasi)),
            $row_aplikasi->cdcabang,
            $row_aplikasi->namacabang,
            $row_aplikasi->nip,
            $row_aplikasi->nama_user,
            $row_aplikasi->namajabatan,
            $row_aplikasi->nama_nasabah,
            $row_aplikasi->norek,
            $row_aplikasi->nocif,
            $row_aplikasi->tgllahir,
            $row_aplikasi->jnsnasabah,
            $row_aplikasi->nominal_aplikasi,
            $row_aplikasi->ktpnasabah,
            $row_aplikasi->produk,
            $row_aplikasi->jnspinjaman,
            $row_aplikasi->jnsusaha,
            $row_aplikasi->lamausaha,
            $row_aplikasi->jnsjaminan,
            $row_aplikasi->pic_nasabah,
            $row_aplikasi->telpnasabah,
            $row_aplikasi->namatoko,
            $row_aplikasi->kategori,
            $row_aplikasi->catatan,
            $row_aplikasi->keterangan,
            $data_pic->nama_user,
            $data_pic->nomorhp,
            $row_aplikasi->namaproduct,
            $row_aplikasi->progress,
            $row_aplikasi->progress_note
        ];
    }
} else {
    // 无数据时填充空列,保持表头对齐
    $excel_data[] = array_merge(['No Record Found'], array_fill(1, 28, ''));
}
// 批量写入数据,从A2开始
$objPHPExcel->getActiveSheet()->fromArray($excel_data, null, 'A2');
// --- 数据写入结束 ---

3. 其他辅助优化

数据库查询优化

你的get_aplikasi_m模型代码不完整,缺少返回语句,而且用了select('*')会查询不必要的字段,增加数据传输和内存占用。修改如下:

function get_aplikasi_m() { 
    $periode = $this->input->post("bulan_kinerja"); 
    $idcabang = $this->input->post("id_cabang"); 
    $cek_cabang = $this->cek_cabang_m($idcabang); 

    // 只选择需要的字段,避免查询冗余数据
    $this->db->select('
        tbl_aplikasi.tglaplikasi, tbl_aplikasi.cdcabang, tbl_aplikasi.namacabang,
        tbl_aplikasi.nip, tbl_user.nama_user, tbl_aplikasi.namajabatan,
        tbl_aplikasi.nama_nasabah, tbl_aplikasi.norek, tbl_aplikasi.nocif,
        tbl_aplikasi.tgllahir, tbl_aplikasi.jnsnasabah, tbl_aplikasi.nominal_aplikasi,
        tbl_aplikasi.ktpnasabah, tbl_aplikasi.produk, tbl_aplikasi.jnspinjaman,
        tbl_aplikasi.jnsusaha, tbl_aplikasi.lamausaha, tbl_aplikasi.jnsjaminan,
        tbl_aplikasi.pic_nasabah, tbl_aplikasi.telpnasabah, tbl_aplikasi.namatoko,
        tbl_aplikasi.kategori, tbl_aplikasi.catatan, tbl_aplikasi.keterangan,
        tbl_aplikasi.idpic, tbl_aplikasi.namaproduct, tbl_aplikasi.progress,
        tbl_aplikasi.progress_note
    '); 
    $this->db->from("tbl_aplikasi"); 
    $this->db->join("tbl_user", "tbl_user.iduser = tbl_aplikasi.iduser", "Left"); 

    // 加上periode筛选条件(你原来的代码里遗漏了)
    if($periode){
        $this->db->where('YEAR(tbl_aplikasi.tglaplikasi)', date('Y', strtotime($periode)));
        $this->db->where('MONTH(tbl_aplikasi.tglaplikasi)', date('m', strtotime($periode)));
    }
    // 加上idcabang筛选条件
    if($idcabang){
        $this->db->where('tbl_aplikasi.cdcabang', $idcabang);
    }

    return $this->db->get()->result(); 
}

PHPExcel内存与计算优化

如果数据量还是很大,可以开启磁盘缓存减少内存占用,同时

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 11:24:37