2013-12-18 78 views
1

我需要运行一个每日cron作业,该作业遍历一个6 MB CSV文件,以将每个〜10,000个条目插入MySQL表中。我写的代码挂起并在一段时间后产生一个超时。PHP将大型CSV文件导入到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的使用仅限) 。

我搜索了,所以有非常类似的问题,但没有人能解决我的问题。我尝试的是以下内容:

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

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

我无权访问php.ini,因为它是共享的托管帐户(cloudsites),并且只能通过phpMyAdmin访问MySQL

我还有什么可以尝试尽可能有效地完成这项工作?

任何帮助表示赞赏。

+1

我经常处理.csv - > mysql,我的一般策略是每个插入记录多个记录,例如,插入表值(一,二),(三,四),(五,六)等。 – Dave

+1

做一个批量插入会更好,而不是每行一个插入。 – user602525

+0

感谢@Dave,但是我怎样才能在while循环中使用PDO呢?它会使用'mysql_ *',但我不能使用它。 –

回答

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手册,很多配置值可以在运行时从您的代码中设置。

+0

为什么这是低调? – Lasse

+0

普通服务器可以插入至少1k记录/秒。增加时间限制,仍然做错事不是一个有效的解决方案。 – Skpd