2017-05-03 10 views
0

foreachの大規模なExcelファイルをインポートするスクリプトを用意しています.50回繰り返した後にはそれはやや遅くなってしまいます。DBへの大量のExcelインポートがSymfonyで非常に遅くなる

私はこれとそれをできるだけ読みやすくしよう:

foreach worksheet (approx 20) { 
    NEW DB ENTRY, PERSIST, FLUSH (account) 
    foreach row (10-100){ 
     NEW DB ENTRY, PERSIST, FLUSH (object) 
     foreach column (approx. 10){ 
      CREATE NEW DB ENTRY, FOREIGN KEY to 'object', PERSIST, FLUSH (weekdates) 
     } 
     foreach column (approx. 50){ 
      CREATE NEW DB ENTRY, FOREIGN KEY to 'object', PERSIST, FLUSH (scheduleEntry) 

      CREATE NEW DB ENTRY, FOREIGN KEY to 'scheduleEntry', PERSIST, FLUSH (scheduleObject) 

      CREATE NEW DB ENTRY, FOREIGN KEY to 'scheduleObject', PERSIST, FLUSH (scheduleModule) 

      /* WORST CASE IS THAT HERE WE HAVE FLUSHED 100000 times */ 
     } 
    } 
} 

は、特に最後のforeachを固定する方法はありますか?私は新しいものに以前のエントリを外しておく必要があるので、毎回フラッシュする必要があると思います、そうですか?遅いとは、Excelファイルにインポートするのに24時間以上かかることを意味します。それは例の数字についてだった。

実際の(まだsimplyfied)コードを使用すると、EntityManagerのは、UnitOfWorkの中に保存され、現在は「管理」の実体となっているを通じて持続/作成この

/* Create Excel */ 
$excel = $this->getContainer()->get('phpexcel')->createPHPExcelObject(Constants::FULL_PATH . 'excel/touren_' . $filename . '.xls'); 
$sheets = $excel->getAllSheets(); 
foreach ($sheets as $id => $sheet) { 
    $ws = $sheet->toArray(); 

    /* Read sth from first line and create an 'account' from this */ 
    $n = new Network(); 
    .... 
    $em->persist($n); 

    try { 
     $em->flush(); 
     $output->writeln('----><info>Inserted in DB</info>'); 
    } catch (Exception $e) { 
     $output->writeln('----><error>DB ERROR</error>'); 
    } 

    /* Go through all rows of current WorkSheet */ 
    foreach ($ws as $row) { 
     /* Create new Object */ 
     $object = new Object(); 
     ... 
     $em->persist($object); 

     try { 
      $em->flush(); 
      $output->writeln("------->Save Object to DB: <info>OK</info>"); 
     } catch (\Exception $e) { 
      $output->writeln("------->Save Object to DB: <error>Failed: " . $e->getMessage() . "</error>"); 
     } 

     /* Create new Tour for weekday/client */ 
     $tour = new Tour(); 
     $tour->setNetwork($n); 

     /* More foreach */ 
     foreach ($clientKey as $filialNo => $filialKey) { 
      $tourObject = new TourObject(); 
      $tourObject->setTour($tour); 
      $tourObject->setObject($o); 
      $em->persist($tourObject); 


     /* Count Intervals */ 
     foreach ($filialKey as $tasks) { 
      if (!$tourObject->getModule()->contains($module)) { 
       $tourObject->addModule($module); 
       $em->persist($tourObject); 

       /* More foreach */ 
       foreach ($period as $date) { 
        $schedule = new Schedule(); 
        $schedule->setTour($tour); 
        .... 
        $em->persist($schedule); 
        try { 
         $em->flush(); 
         $output->writeln("------->Save Schedule to DB: <info>OK</info>"); 
        } catch (\Exception $e) { 
         $output->writeln("------->Save Schedule to DB: <error>Failed: " . $e->getMessage() . "</error>"); 
        } 


        $scheduleObject = new ScheduleObject(); 
        $scheduleObject->setSchedule($schedule); 
        .... 
        $em->persist($scheduleObject); 
        try { 
         $em->flush(); 
         $output->writeln("------->Save ScheduleObject to DB: <info>OK</info>"); 
        } catch (\Exception $e) { 
         $output->writeln("------->Save ScheduleObject to DB: <error>Failed: " . $e->getMessage() . "</error>"); 
        } 

        $scheduleObjectModule = new ScheduleObjectModule(); 
        $scheduleObjectModule->setScheduleObject($scheduleObject); 
        $em->persist($scheduleObjectModule); 
        try { 
         $em->flush();                
         $output->writeln("------->Save ScheduleObjectModule to DB: <info>OK</info>"); 
        } catch (\Exception $e) { 
         $output->writeln("------->Save ScheduleObjectModule to DB: <error>Failed: " . $e->getMessage() . "</error>"); 
        } 
       } 
      } 
     } 
     } 

     /* Flush all?!? */ 
     try { 
      $em->flush(); 
      $output->writeln("------->Save Task to DB: <info>OK</info>"); 
     } catch (\Exception $e) { 
      $output->writeln("------->Save Task to DB: <error>Failed: " . $e->getMessage() . "</error>"); 
     } 
    } 
+0

頻繁にフラッシュする必要はありません。たぶん100ループごとにそうです。他に何を試しましたか?ほぼ同じ数百のスタックオーバーフローの質問があります。 – Cerad

答えて

2

すべてのエンティティのようにかなったように見えます。このUnitOfWorkがいっぱいになると、かなり重いシステムになります。各 "sheet"の後に$ entityManager-> clear()を呼び出して、各繰り返しの後にUoWがクリアされるようにすることができます。

各エンティティは独自のUnitOfWorkを持っていますが、各エンティティごとにUoWを個別にクリアすることはできますが、たくさんのエンティティを作成するので、エンティティクラスを指定せずにすべてクリアしてください。

... 
    /* Flush all?!? */ 
    try { 
     $em->flush(); 
     $em->clear(); 
     $output->writeln("------->Save Task to DB: <info>OK</info>"); 
    } catch (\Exception $e) { 
     $output->writeln("------->Save Task to DB: <error>Failed: " . $e->getMessage() . "</error>"); 
    } 

またはあなたのDBに挿入するためにネイティブクエリを使用することができ、それは常にあなたがデータの一貫性etc.etcの面で望むものではないかもしれません。

上記で指摘したように、各エンティティの後にフラッシュする必要はありません。一回だけflushを呼び出すと、各 'sheet'の後に、Doctrineはすべてのinsert文を一度に実行します。

0

私は、これはあなたがPHPで書くことができます何よりもはるかに高速になるだろう

(のようなMysql Load data infile)良い解決策は、このためにネイティブDBユーティリティを使用することだと思います。

関連する問題