【问题标题】:PHP Import large CSV file into MySQL tablePHP将大型CSV文件导入MySQL表
【发布时间】:2014-01-06 18:52:00
【问题描述】:

我需要运行一个每日 cron 作业,该作业迭代一个 6 MB 的 CSV 文件,以将大约 10,000 个条目中的每一个插入到 MySQL 表中。我编写的代码挂起并在一段时间后产生超时。

if (($handle = fopen($localCSV, "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        $dbdata = array(
            'SiteID' => $siteID,
            'TimeStamp' => $data[0],
            'ProductID' => $data[1],
            'CoordX' => $data[2],
            'CoordY' => $data[3]
        );  
        $row++;
        $STH = $DBH->prepare("INSERT INTO temp_csv (SiteID,TimeStamp,ProductID,CoordX,CoordY) VALUES (:SiteID,:TimeStamp,:ProductID,:CoordX,:CoordY)");
        $STH->execute($dbdata);
    }
    fclose($handle);
    echo $row." rows inserted.";
}

最好使用mysql_* 函数而不是 PDO,因此我可以将这些值内爆到一个查询中(虽然很大),但不幸的是我需要遵守一些准则(要严格使用 PDO)。

我搜索了 SO 并且有非常相似的问题,但没有一个可以解决我的问题。我尝试的是以下内容:

1- 运行 LOAD DATA INFILELOAD DATA LOCAL INFILE 查询,但不断收到“找不到文件”错误,尽管该文件确实存在 777 权限。数据库服务器和共享主机帐户位于不同的环境中。我尝试了 csv 文件的相对路径和 url 路径,但没有运气(在这两种情况下都找不到该文件)。

2- 我将 csv 文件拆分为 2 个文件并在每个文件上运行脚本,以查看脚本挂起的阈值,但在每个文件的情况下,它在表中插入了两次条目。

我无权访问php.ini,因为它是一个共享主机帐户(云站点),只能通过 phpMyAdmin 访问MySQL

我还能尝试什么来尽可能高效地完成此任务?

感谢任何帮助。

【问题讨论】:

  • 我经常处理 .csv -> mysql,我的一般策略是每次插入多条记录,例如INSERT INTO table VALUES(一、二)、(三、四)、(五、六)等
  • 批量插入会比每行插入一个更好。
  • 谢谢@Dave,但是我怎么能在while循环中使用PDO呢?它可以与 mysql_* 一起使用,但我不能使用它。
  • $DBH->prepare调用移出循环。

标签: php mysql sql csv pdo


【解决方案1】:

代码在我看来并没有错。它挂起,因为它只需要一段时间来执行。您应该使用 phps set_time_limit 来防止超时。

if (($handle = fopen($localCSV, "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
    set_time_limit(30) // choose a value that works for you
    // ... the rest of your script

不过,更好的是启动一个处理 csv 的后台进程,它需要某种锁定,因此它不会在多个实例中并行运行。如果您将状态写入磁盘上的文件,您可以轻松地将其呈现给您的用户。 这同样适用于 cron 脚本(如果您可以使用托管解决方案做到这一点)

对我来说,使用 PDO 看起来不错。我不会考虑一次插入 csv 的所有行,但您也可以使用 PDO 一次插入多行。为多行创建语句和数据数组。它可能看起来像这个粗略的草图(我没有执行它,所以可能会有一些错误):

function insert_data($DBH, array $dbdata, array $values) {
    $sql = "INSERT INTO temp_csv (SiteID,TimeStamp,ProductID,CoordX,CoordY) VALUES %1$s;";
    $STH = $DBH->prepare(sprintf($sql, join(', ', $values)));
    $STH->execute($dbdata);
}

if (($handle = fopen($localCSV, "r")) !== FALSE) {
    $dbdata = array();
    $values = array();
    $row = 0;
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        if(!count($dbdata))
            $dbdata['SiteID'] = $siteID;

        $dbdata['TimeStamp_'.$row] = $data[0];
        $dbdata['ProductID_'.$row] = $data[1];
        $dbdata['CoordX_'.$row] = $data[2];
        $dbdata['CoordY_'.$row] = $data[3];
        $values[] = sprintf('(:SiteID_%1$s,:TimeStamp_%1$s,:ProductID_%1$s,:CoordX_%1$s,:CoordY_%1$s)', $row);
        $row++;

        if($row % 10 === 0) {
            set_time_limit(30);
            insert_data($DBH, $dbdata, $values);
            $values = array();
            $dbdata = array();
        }
    }
    // insert the rest
    if(count($values))
        insert_data($DBH, $dbdata, $values);
    fclose($handle);
    echo $row." rows inserted.";
}

至少读取 php.ini 配置的快捷方式是phpinfo。查看 PHP 手册,很多配置值可以在运行时从您的代码中设置。

【讨论】:

  • 常规服务器至少可以插入1k条记录/秒。增加时间限制并仍然做错事不是有效的解决方案。
猜你喜欢
  • 2012-09-19
  • 1970-01-01
  • 2011-04-16
  • 2014-03-15
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
  • 2019-09-17
  • 1970-01-01
相关资源
最近更新 更多