2017-08-18 17 views
6

CSVファイルからデータを読み取るスクリプトを作成し、データがデータベースに既に存在するかどうかをチェックし、そうでない場合はインポートします。データが存在する場合(特定の製品のコード)、残りの情報はCSVファイルから更新する必要があります。CSVからデータベースに存在しないデータのみをインポートする

たとえば、 CSVファイルにAlexという名前とJohnsonという名前のコードWTW-2LTのメンバーがあります。このスクリプトは、WTW-2LTというコードのAlexとJohnsonという名前のメンバーが既に存在するかどうかをチェックし、連絡先の詳細と追加の詳細をスクリプトから更新する必要があるかどうかを確認します(件名や講師、すべての詳細がCSVの1行にあります)、存在しなければ新規メンバーを作成するだけです。

私のスクリプト今のところ気を散らすことを防ぐために最小限のチェックを行っています。

while ($row = fgetcsv($fp, null, ";")) { 
    if ($header === null) { 
     $header = $row; 
     continue; 
    } 

    $record = array_combine($header, $row); 

    $member = $this->em->getRepository(Member::class)->findOneBy([ 
     'code' =>$record['member_code'], 
     'name' =>$record['name'], 
     'surname' =>$record['surname'], 
    ]); 

    if (!$member) { 
     $member = new Member(); 
     $member->setCode($record['member_code']); 
     $member->setName($record['name']); 
     $member->setName($record['surname']); 
    }  
    $member->setContactNumber($record['phone']); 
    $member->setAddress($record['address']); 
    $member->setEmail($record['email']); 

    $subject = $this->em->getRepository(Subject::class)->findOneBy([ 
     'subject_code' => $record['subj_code'] 
    ]); 

    if (!$subject) { 
     $subject = new Subject(); 
     $subject->setCode($record['subj_code']); 
    } 
    $subject->setTitle($record['subj_title']); 
    $subject->setDescription($record['subj_desc']); 
    $subject->setLocation($record['subj_loc']); 

    $lecturer = $this->em->getRepository(Lecturer::class)->findOneBy([ 
     'subject' => $subject, 
     'name' => $record['lec_name'], 
     'code' => $record['lec_code'], 
    ]); 

    if (!$lecturer) { 
     $lecturer = new Lecturer(); 
     $lecturer->setSubject($subject); 
     $lecturer->setName($record['lec_name']); 
     $lecturer->setCode($record['lec_code']); 
    } 
    $lecturer->setEmail($record['lec_email']); 
    $lecturer->setContactNumber($record['lec_phone']); 

    $member->setLecturer($lecturer); 

    $validationErrors = $this->validator->validate($member); 
    if (!count($validationErrors)) { 
     $this->em->persist($member); 
     $this->em->flush(); 
    } else { 
     // ... 
    } 
} 

このスクリプトは、データベースに3回問い合わせて1つのCSV行が存在するかどうかを確認する必要があることがわかります。私の場合は、最大2000行以上のファイルがあるので、すべての行が3行のクエリを実行してその行が存在するかどうかを確認するのはかなり時間がかかります。

残念ながら、1つのサブジェクトが存在しない場合は、バッチがデータベースにフラッシュされ、ポイントを持たない重複レコードがあるまで何度も作成されるため、バッチでローをインポートすることもできません。

パフォーマンスとスピードを最大限に向上させるにはどうすればよいですか?最初にデータベースからすべてのレコードを取得し、配列に格納して(メモリを消費しますか?)、チェックを行い、配列に行を追加してそこからチェックします。

答えて

6

正直言って、私はその3倍の量のクエリを2000回以上見つけられません。しかし、あなたがパフォーマンスを求めているので、ここに私の2セントです:

フレームワークを使用すると、常にオーバーヘッドが発生します。つまり、ネイティブPHPでこのコードを作成すると、すでに実行されています。私はsymfonyに慣れていませんが、データをデータベースに保存すると仮定します。 MySQLでは、コマンドINSERT ... ON DUPLICATE KEY updateを使用することができます。 3つのフィールド(コード、名前、姓)をプライマリキー(私が前提とする)に設定した場合は、それを使用してデータを挿入できますが、キーがすでに存在する場合はデータベースの値を更新します。 MySQLはあなたのためにchecskを実行して、データが変更されたかどうかを確認します。もしそうでなければ、diskwriteは起こりません。

私はsymfonyにネイティブSQLを書くことができますので、フレームワークが提供するセキュリティを使用できるようになりますが、挿入を高速化できます。

+0

うーん、OK、私は私の推測タイムアウトは主に問題であるため、 'set_time_limit'の使用に慣れる必要があります。 – Mentos93

+0

制限時間を変更しない場合は、行を個々のSQLクエリに変換するスクリプトを作成できます。クエリーをtxtファイルとして保存してサーバーにアップロードするよりも、 phpでは、ファイルからX行を読み込み、データベース上でそれらを実行し、最後に実行した行を保存することができます。次回以降はその行から続行します。しかし、このようにするにはさらに多くの作業が必要ですが、サーバー負荷のバランスをさらに取ることになります。 – Jeffrey

2

パフォーマンスを向上させたい場合は、すべてのデータをデータベースにダンプし、そこでSQL文を使用して変換してください。 DBSはこのようにすべてのステップを最適化することができます。

次の例のように、コマンドは、あなたのCSVファイルの形式を指定することができる多くのオプションを持っている

LOAD DATA INFILE 'data.csv' 
INTO TABLE tmp_import 

SQLコマンドを使用してMySQLデータベースに直接CSV-ファイルをインポートすることができます。

data.csvがすべての古い行と新しい行を含む完全なダンプの場合、ちょっと修正した後、現在の表をインポートした表と置き換えることができます。 member_codeがある場合

SELECT member_code, name, surname 
FROM tmp_import 
GROUP BY member_code, name, surname 

例えば、それはあなたのCSVファイル(およびインポートテーブル)のように見えるあなたはその後、グループ化を経て個別の行を得ることができるビット

WTW-2LT, Alex, Johnson, subj_code1, ..., lec_name1, ... 
WTW-2LT, Alex, Johnson, subj_code1, ..., lec_name2, ... 
WTW-2LT, Alex, Johnson, subj_code2, ..., lec_name3, ... 
WTW-2LU, John, Doe,  subj_code3, ..., lec_name4, ... 

のようになります。鍵はGROUP BY member_codeです。 DBSは、それが技術的にはagainst the standardだと信じても、文句を言うことはありません。あなたが同じことを行う、あなたの残りのデータを取得するには

subj_codelec_codeを想定し

SELECT subj_code, subj_title, member_code 
FROM tmp_import 
GROUP BY subj_code 

SELECT lec_code, lec_name, subj_code 
FROM tmp_import 
GROUP BY lec_code 

は、両方の科目や講義のためのキーです。

INSERT INTO members (member_code, name, surname) 
SELECT member_code, name, surname 
FROM tmp_import_members 
WHERE tmp_import_members.member_code NOT IN (
    SELECT member_code FROM members WHERE member_code IS NOT NULL 
); 

UPDATE members 
JOIN tmp_import_members ON 
    members.member_code = tmp_import_members.members_code 
SET 
    members.name = tmp_import_members.name, 
    members.surname = tmp_import_members.surname; 

と:あなたは、2つのクエリでinsertsupdatesを行うことができます

CREATE TABLE tmp_import_members 
SELECT member_code, name, surname 
FROM tmp_import 
GROUP BY member_code, name, surname 

は、実際に、たとえば、あなたがMySQLのCREATE TABLE ... SELECT -syntaxを使用することができますテーブルとして、この結果を保存するにはあなたの好きなものに対する講義と同じです。

これは非常に高速である必要がありますあなたのCSVファイルの

  • 1つの一括インポート、あなたのメンバー、科目や講義のための
  • 3一時テーブル、
  • 3の挿入と3更新するすべての金額ステートメント(テーブルごとに1)あなたの一時テーブル上の
  • 一滴テーブルあなたは再び

を終わった後:あなたのC場合SV-Fileには、既存のテーブルを置き換えて3つの挿入と3つの更新を保存できるすべての行が含まれています。

上記のクエリでMySQLがNOT INJOINを高速化できるように、一時テーブルの関連する列にインデックスを作成するようにしてください。

1

あなたはデータがあなたのテーブルに存在しているかどうかを見つけることができる数に基づいてその後のすべての3つのテーブル

SELECT 
    (SELECT COUNT(*) FROM member WHERE someCondition) as memberCount, 
    (SELECT COUNT(*) FROM subject WHERE someCondition) as subjectCount, 
    (SELECT COUNT(*) FROM lecturer WHERE someCondition) as lecturerCount 

からカウントを取得するために最初にカスタムSQLを実行することができます。あなたは教義でカスタムSQLを実行する方法を知っているネイティブSQL

チェックアウト、このリンクに行けば、一意のクエリを複数回実行する必要はありません

Symfony2 & Doctrine: Create custom SQL-Query

関連する問題