PhpSpreadsheet:重复MySQL记录写入Excel对应列的实现问题
解决重复DNI的Excel写入分组问题
核心需求回顾
已实现从MySQL导出数据到Excel,现有规则:
- 当
tipoActividad=1时,写入DESCRIPCION和DIA到对应列组 - 当
tipoActividad=2时,写入NOMBRE_SESION和DIA到对应列组 - 需补充逻辑:若Excel中已存在某DNI的行,将当前记录写入该行的下一组空列(例:AE、AF已填充时,写入AG、AH)
MySQL表结构示例
CREATE TABLE actividades ( id INT AUTO_INCREMENT PRIMARY KEY, DNI VARCHAR(20) NOT NULL, tipoActividad TINYINT(1) NOT NULL, DESCRIPCION TEXT, NOMBRE_SESION VARCHAR(100), DIA DATE NOT NULL );
解决方案(基于PhpSpreadsheet实现)
核心思路
- 先遍历Excel现有数据,构建DNI-行号映射数组,快速定位重复DNI的位置
- 处理每条MySQL记录时:
- 若DNI已存在:在对应行中从初始列组开始,找到第一个双列都为空的组写入
- 若DNI不存在:新增一行,写入DNI和第一组数据,同时更新映射数组
修改后的PHP代码
<?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // 初始化Excel对象(读取已有文件请用IOFactory::load()) $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // 配置参数:表头在第1行,数据从第2行开始;初始列组为AE(索引30)、AF(索引31)(PhpSpreadsheet列索引从0开始) $startRow = 2; $initialColGroupStart = 30; $highestRow = $sheet->getHighestRow(); // 1. 构建DNI-行号映射 $dniRowMap = []; for ($row = $startRow; $row <= $highestRow; $row++) { $dni = $sheet->getCellByColumnAndRow(0, $row)->getValue(); // 假设DNI在A列(索引0) if (!empty($dni)) { $dniRowMap[$dni] = $row; } } // 2. 从MySQL获取数据 $pdo = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'password'); $stmt = $pdo->query("SELECT DNI, tipoActividad, DESCRIPCION, NOMBRE_SESION, DIA FROM actividades"); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); // 3. 逐条处理记录 foreach ($records as $record) { $dni = $record['DNI']; $tipo = $record['tipoActividad']; $dia = $record['DIA']; $content = $tipo == 1 ? $record['DESCRIPCION'] : $record['NOMBRE_SESION']; if (isset($dniRowMap[$dni])) { // DNI已存在,查找该行下一组空列 $targetRow = $dniRowMap[$dni]; $currentCol = $initialColGroupStart; while (true) { $col1Val = $sheet->getCellByColumnAndRow($currentCol, $targetRow)->getValue(); $col2Val = $sheet->getCellByColumnAndRow($currentCol + 1, $targetRow)->getValue(); if (empty($col1Val) && empty($col2Val)) { // 找到空组,写入数据 $sheet->setCellValueByColumnAndRow($currentCol, $targetRow, $content); $sheet->setCellValueByColumnAndRow($currentCol + 1, $targetRow, $dia); break; } $currentCol += 2; // 跳到下一组列 } } else { // DNI不存在,新增行写入 $targetRow = ++$highestRow; // 写入DNI到A列 $sheet->setCellValueByColumnAndRow(0, $targetRow, $dni); // 写入第一组数据 $sheet->setCellValueByColumnAndRow($initialColGroupStart, $targetRow, $content); $sheet->setCellValueByColumnAndRow($initialColGroupStart + 1, $targetRow, $dia); // 更新映射 $dniRowMap[$dni] = $targetRow; } } // 保存文件 $writer = new Xlsx($spreadsheet); $writer->save('exported_actividades.xlsx'); $spreadsheet->disconnectWorksheets(); unset($spreadsheet); ?>
关键细节说明
- 映射数组优化:
$dniRowMap避免了每次查找重复DNI时遍历整个Excel,大幅提升处理效率 - 列组判断:只有当一组的两个单元格都为空时才视为可写入的空组,避免覆盖已有数据
- 列索引对应:PhpSpreadsheet使用0-based列索引,需注意Excel列名与索引的转换(如AE列对应索引30)
内容的提问来源于stack exchange,提问作者rek
相关产品推荐
相关产品推荐

