2013-12-18 4 views
1

Мне нужно запустить ежедневное задание cron, которое выполняет итерацию по файлу CSV размером 6 МБ, чтобы вставить каждый из ~ 10000 записей в таблицу 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 быть строго используется) ,

Я искал SO и есть очень похожие вопросы, но никто не смог решить мою проблему. То, что я пробовал, является следующим:

1- Ran LOAD DATA INFILE и LOAD DATA LOCAL INFILE запросов, но при этом сохраняются ошибки «файл не найден», хотя файл определенно существует с 777 правами доступа. Сервер БД и общая учетная запись хостинга находятся в разных средах. Я пробовал относительные и URL-пути к файлу csv, но не повезло (не удалось найти файл в обоих случаях).

2- Я разбил файл csv на 2 файла и запускал сценарий для каждого, чтобы увидеть порог, на котором висит скрипт, но он вставил записи дважды в таблицу в случае каждого файла.

У меня нет доступа к php.ini, так как это общая учетная запись (облачные сайты) и доступ к MySQL через phpMyAdmin.

Что еще я могу попытаться сделать это максимально эффективно?

Любая помощь приветствуется.

+1

Я занимаюсь с .csv -> mysql часто, и моя общая стратегия заключается в том, чтобы делать несколько записей на вставку, например. ВСТАВИТЬ В СТОИМОСТЬ ЗНАЧЕНИЙ (один, два), (три, четыре), (пять, шесть) и т. Д. – Dave

+1

Выполнение пакетной вставки будет лучше, чем отдельная вставка для каждой строки. – user602525

+0

Спасибо @Dave, но как я могу это сделать с PDO в цикле while? Он работал бы с 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

Обычный сервер может вставлять не менее 1 тыс. Записей/сек. Увеличение срока и все еще неправильное действие не является допустимым решением. – Skpd

Смежные вопросы