PHP将大型CSV文件导入MySQL表

时间:2013-12-18 14:26:16

标签: 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- Ran LOAD DATA INFILELOAD DATA LOCAL INFILE查询但仍然收到“找不到文件”错误,尽管该文件肯定存在777权限。数据库服务器和共享主机帐户位于不同的环境中。我尝试了相对和url路径到csv文件,但没有运气(在两种情况下都找不到文件)。

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

我无法访问php.ini,因为它是共享主机帐户(Cloudsites),只能通过phpMyAdmin访问MySQL

我还能尽可能有效地尝试实现这一目标吗?

感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

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