如何使用PHP更有效地将csv文件导入MySQL数据库?

时间:2017-03-01 15:30:55

标签: php mysql symfony csv doctrine

我解释一下,我有一个Symfony2项目,我需要通过我的数据库中的csv文件导入用户。在MySQL中导入数据之前,我必须对数据做一些工作。我为此创建了一个服务,一切正常,但是如果我将整个文件都给它,那么执行和减慢我的服务器需要花费太多时间。我的文件通常在500到1500行之间,我必须将文件拆分成~200行文件并逐个导入。

我需要处理已经存在于文件和/或数据库中的相关用户。相关用户通常是孩子的父母。

这是我的简化代码:

$validator = $this->validator;

$members = array();
$children = array();
$mails = array();

$handle = fopen($filePath, 'r');
$datas = fgetcsv($handle, 0, ";");

while (($datas = fgetcsv($handle, 0, ";")) !== false) {

    $user = new User();

    //If there is a related user
    if($datas[18] != ""){
        $user->setRelatedMemberEmail($datas[18]);

        $relation = array_search(ucfirst(strtolower($datas[19])), UserComiti::$RELATIONSHIPS);
        if($relation !== false)
            $user->setParentRelationship($relation);
    }
    else {
        $user->setRelatedMemberEmail($datas[0]);
        $user->addRole ( "ROLE_MEMBER" );
    }

    $user->setEmail($mail);
    $user->setLastName($lastName);
    $user->setFirstName($firstName);
    $user->setGender($gender);
    $user->setBirthdate($birthdate);
    $user->setCity($city);
    $user->setPostalCode($zipCode);
    $user->setAddressLine1($adressLine1);
    $user->setAddressLine2($adressLine2);
    $user->setCountry($country);
    $user->setNationality($nationality);
    $user->setPhoneNumber($phone);

    //Entity Validation
    $listErrors = $validator->validate($user);

    //In case of errors
    if(count($listErrors) > 0) {
         foreach($listErrors as $error){
              $nbError++;
              $errors .= "Line " . $line . " : " . $error->getMessage() . "\n";
         }
   }

   else {
       if($mailParent != null)
            $children[] = $user;

       else{
            $members[] = $user;
            $nbAdded++;
       }
   }

   foreach($members as $user){
        $this->em->persist($user);
        $this->em->flush();
   }

   foreach($children as $child){

       //If the related user is already in DB
       $parent = $this->userRepo->findOneBy(array('username' => $child->getRelatedMemberEmail(), 'club' => $this->club));

       if ($parent !== false){

           //Check if someone related to related user already has the same last name and first name. If it is the case we can guess that this user is already created
           $testName = $this->userRepo->findByParentAndName($child->getFirstName(), $child->getLastName(), $parent, $this->club);

           if(!$testName){
                $child->setParent($parent);
                $this->em->persist($child);
                $nbAdded++;
           }
           else
                $nbSkipped++;
       }

       //Else in case the related user is neither file nor in database we create a fake one that will be able to update his profile later.
       else{

            $newParent = clone $child;
            $newParent->setUsername($child->getRelatedMemberEmail());
            $newParent->setEmail($child->getRelatedMemberEmail());
            $newParent->setFirstName('Unknown');

            $this->em->persist($newParent);
            $child->setParent($newParent);
            $this->em->persist($child);

            $nbAdded += 2;
            $this->em->flush();
        }
    }
}

这不是我的全部服务,因为我不认为剩下的会有所帮助,但如果您需要更多信息,请问我。

1 个答案:

答案 0 :(得分:1)

虽然我没有采用定量方法来确定程序中的瓶颈,但我可以提出一些可能会显着提高其性能的指南。

  1. 最大限度地减少您正在进行的数据库提交的数量。写入数据库时​​会发生很多事情。是否可以在结束时只提交一次?

  2. 最大限度地减少您正在进行的数据库读取次数。与前一点类似,当您从数据库中读取时会发生很多事情。

  3. 如果在考虑上述几点后仍然存在问题,请确定ORM实际生成和执行的SQL。 ORM工作得很好,直到效率成为一个问题,并且需要更多的关注来确保生成最佳查询。此时,更熟悉ORM和SQL将是有益的。

    您似乎没有使用太多数据,但如果您使用,MySQL本身就支持读取CSV文件。

      

    LOAD DATA INFILE语句以非常高的速度将文本文件中的行读取到表中。   https://dev.mysql.com/doc/refman/5.7/en/load-data.html

    您可以通过ORM访问此MySQL特定功能,但如果没有,则需要编写一些纯SQL来使用它。由于您需要修改从CSV中读取的数据,因此您可以通过以下步骤非常快速地执行此操作:

    1. 使用LOAD DATA INFILE将CSV读入临时表。
    2. 根据需要操作临时表和其他表中的数据。
    3. 将临时表中的数据选择到目标表中。