使用 PHP/MySQL 导出大型 CSV 数据的最佳方法是啥?

Posted

技术标签:

【中文标题】使用 PHP/MySQL 导出大型 CSV 数据的最佳方法是啥?【英文标题】:What is the best approach to export large CSV data using PHP/MySQL?使用 PHP/MySQL 导出大型 CSV 数据的最佳方法是什么? 【发布时间】:2014-05-26 05:51:58 【问题描述】:

我正在做一个项目,我需要从包含近 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() 写你的行吗? 从查询结果集中单独读取每个数据行并直接写入php://output,然后读取下一行等;而不是构建任何大型数组或在内存中构建 csv 1.您是否使用echo 向浏览器发送数据? 2. 您是否要求浏览器通过 HTTP 标头下载文件 3. 您如何从数据库中获取数据? 您是否考虑过使用mysqldump system 命令。这解决了我过去的所有问题。 @rosscowar mysqdump 不适用于我,因为我需要在添加到 CSV 之前格式化数据。 【参考方案1】:

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

详细描述:试试这个导出一个带有列名的大表(我用过,效果很好,它也可以改进和压缩和优化,但是……以后):

    打开 CSV 文件(标题、fopen 等) 用列名定义一个数组和:fputcsv($f, $line, $delimiter); 获取你想要的 id 列表(不是整行,只有 id):SELECT id FROM table WHERE condition ORDER BY your_desired_field ASC -> 这里有$ids $perpage = 200; // how many lines you export to csv in a pack;
    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
    
    关闭 CSV

因此,您将遍历整个结果表,获取 200 行并将它们写入 CSV,CSV 将等待打开,直到您写入所有行。您需要的所有内存都是 200 行,因为您将重新编写变量。我确信它可以以更好的方式完成,但对我来说花了几个小时并且没有找到解决方案;此外,它受到我的架构和应用程序需求的轻微影响,这就是我选择这个解决方案的原因。

【讨论】:

【参考方案2】: 从查询结果集中单独读取每个数据行 直接写入php://output 然后读取下一行等;

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

【讨论】:

请正确格式化您的答案。如果可能,添加代码示例。 嗯是评论【参考方案3】:

如果您真的必须在 PHP 中进行处理,您将需要使用 MYSQL 的 limit 命令来获取数据的子集。每次只抓取一定数量的行,将它们写到文件中,然后抓取下一组。

您可能需要在查询循环中的一些变量上运行 unset()。关键是不要一次在内存中拥有太多巨大的数组。

如果您要抓取整个合并表,请按插入日期升序对它们进行排序,以便第二次抓取将获得任何较新的项目。

【讨论】:

【参考方案4】:

正如评论中所解释的:https://***.com/a/12041241/68567 使用 mysqldump 可能是最好的选择。如果需要,您甚至可以使用 exec() 命令通过 php 执行此操作,如下所述:php exec() - mysqldump creates an empty file

【讨论】:

mysqldump 不适用于我的问题,因为我的 mysql 查询涉及表关系,而且我需要在将数据打印到 CSV 之前对其进行格式化。

以上是关于使用 PHP/MySQL 导出大型 CSV 数据的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

动态构建大型数据框(spark 或 pandas)以导出到 csv 的方法

Ruby-on-Rails 3.2:导出包含大型数据集(100,000 条记录)的 CSV

使用 PHP/PDO 将大型 MySQL 表导出为 CSV

Laravel DataTables 大型导出

macmatlab导入大型csv

从 php mysql 中的 csv 文件的标题中删除反斜杠“\”