PHP/MySQL导出大型CSV数据的最佳方法是什么?

7
我正在做一个项目,需要从包含近10k行的数据库中提取数据,并将其导出为CSV。我尝试了常规方法来下载CSV,但我总是遇到内存限制问题,即使我们已经将memory_limit设置为256MB。
如果你们中有人遇到过相同的问题,请分享你们的想法和最佳解决方案或方法。
非常感谢大家的意见。
这是我的实际代码:
$filename = date('Ymd_His').'-export.csv';

//output the headers for the CSV file
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename={$filename}");
header("Expires: 0");
header("Pragma: public");

//open the file stream
$fh = @fopen( 'php://output', 'w' );

$headerDisplayed = false;

foreach ( $formatted_arr_data_from_query as $data ) {
    // Add a header row if it hasn't been added yet -- using custom field keys from first array
    if ( !$headerDisplayed ) {
        fputcsv($fh, array_keys($ccsve_generate_value_arr));
        $headerDisplayed = true;
    }

    // Put the data from the new multi-dimensional array into the stream
    fputcsv($fh, $data);
}

// Close the file stream
fclose($fh);

你使用未缓存的查询吗?你使用 fputcsv() 写入行数据吗? - VMai
2
逐行从查询结果集中单独读取每个数据行,并直接写入到php://output,然后读取下一行,以此类推;而不是构建任何大型数组或在内存中构建csv。 - Mark Baker
1
你考虑过使用 mysqldumpsystem 命令吗?这在过去解决了我所有的问题。 - rfoo
https://dev.mysql.com/doc/refman/5.1/en/mysqldump.html - rfoo
1
@rosscowar mysqdump 对我来说不适用,因为我需要在添加到 CSV 之前对数据进行格式化。 - eugene a.
显示剩余2条评论
4个回答

3
如果您必须在PHP中进行处理,您需要使用MYSQL的limit命令来获取数据子集。每次只获取一定数量的行,将其写入文件,然后获取下一组。
在查询循环内部,您可能需要对一些变量运行unset()。关键是不要一次在内存中拥有太多巨大的数组。
如果您正在获取整个合并表,请按插入日期升序排序,以便第二次获取任何更新的项目。

2

简要描述:将数百行的导出包重新使用变量导出为CSV文件,以保持内存压力低。您不能将整个mysql表格放入数组中(然后转换为CSV文件),这是主要问题。

长描述:尝试使用以下方法导出具有列名称的大型表格(我使用过它,效果很好,也可以进行改进、压缩和优化,但是...稍后再说):

  1. Open the CSV file (headers, fopen, etc)
  2. Define an array with the column names and: fputcsv($f, $line, $delimiter);
  3. Get a list of ids that you want (not entire rows, only ids): SELECT id FROM table WHERE condition ORDER BY your_desired_field ASC -> here you have $ids
  4. $perpage = 200; // how many lines you export to csv in a pack;
  5. for ($z=0; $z < count($ids); $z += $perpage)
    {
        $q = "SELECT * FROM table WHERE same_condition ORDER BY your_desired_field ASC LIMIT " . $perpage . " OFFSET " . $z 
        // important: use the same query as for retrieving ids, only add limit/offset. Advice: use ORDER BY, don't ignore it, even if you do not really need it;
        $x = [execute query q]
        for ($k=0; $k < count($x); $k++)
        {
            $line = array($x[$k]->id, $x[$k]->field1, $x[$k]->field2 ..);
            fputcsv($f, $line, $delimiter);
        }
    } // end for $z
    
  6. close the CSV

因此,您将循环遍历整个结果表,获取200行并将它们写入CSV文件,直到所有行都写完之前该文件会一直处于等待状态。您所需的所有内存仅用于这200行数据,因为您将重新编写变量。我相信有更好的方法来实现此任务,但对我而言需要几个小时才能找到解决方案;此外,它受到我的架构和应用需求的轻微影响,这就是我选择这种解决方案的原因。


1

2
mysqldump 对于我的问题不适用,因为我的 MySQL 查询涉及表关系,并且在将数据打印到 CSV 前需要格式化数据。 - eugene a.

0
  • 从查询结果集中逐行读取每个数据行
  • 直接写入到php://output
  • 然后读取下一行,以此类推;

而不是构建任何大型数组或在内存中构建csv。


2
请适当格式化您的答案。如有可能,请添加代码示例。 - Mike
那是一条注释。 - M H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接