PHP 将大型 CSV 文件导入 MySQL 表

标签 php mysql sql csv pdo

我需要运行一个每日 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,因为它是一个共享主机帐户(cloudsites)并且只能通过 phpMyAdmin 访问 MySQL

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

感谢任何帮助。

最佳答案

代码在我看来没有错。它挂起是因为它只需要一段时间来执行。你应该使用 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 手册,很多配置值都可以在运行时从您的代码中设置。

关于PHP 将大型 CSV 文件导入 MySQL 表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20660767/

相关文章:

java - 使用 JDBC 在 MySQL 中插入 select

PHP 类:何时使用::vs。 ->?

php - php 无法识别 iframe src

php - 在 PHP 类中的函数之间共享变量

php - Wordpress SQL 选择多个元值/元键/自定义字段

php - 为什么我不应该在 PHP 中使用 mysql_* 函数?

sql - 如果存在重复项,则根据另一列选择值

sql - 查找每个 GROUP BY 结果中的行数

php - 基于子域的 symfony 语言选择

Java MySQL 连接器无法使用 JOIN 子句正确返回表行