2017-08-18 63 views
6

我已經創建了一個腳本,用於從CSV文件讀取數據,檢查數據庫中是否已存在數據,如果沒有,則導入它。如果數據確實存在(特定產品的代碼),則需要從CSV文件更新其餘信息。只從CSV導入不存在的數據到數據庫

例如; 我有一個代碼爲WTW-2LT的會員,在我的CSV文件中命名爲Alex和姓約翰遜。腳本檢查是否存在代碼爲WTW-2LT的成員,名爲Alex和Johnson Johnson已存在,是否存在,聯繫人詳細信息和額外詳細信息是否需要從腳本更新(其他詳細信息,如主題和講師也需要檢查,所有的細節都在CSV中的一行中),如果它不存在,只需創建新成員。

我的腳本到目前爲止,我至少用了其他檢查以防止分心;

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次以檢查是否存在一個CSV行。在我的情況下,我有多達2000多行的文件,因此每一行執行3個查詢來檢查該行是否存在是非常耗時的。

不幸的是,我也無法批量導入行,因爲如果一個主題不存在,它會創建它很多次,直到批處理被刷新到數據庫,然後我坐在重複記錄中,沒有服務點。

如何將性能和速度提高到最大?像首先從數據庫中獲取所有記錄並將其存儲在數組中(內存消耗?),然後執行檢查並將該行添加到數組中,並從那裏檢查...

有人可以幫我找到一種方法提高這個(請用示例代碼?)

回答

6

說實話,我發現2000+行與3倍的查詢量沒有那麼多。但是,既然你要求表現,下面是我的兩分錢:

使用框架總是會產生開銷。這意味着如果你使用本地PHP編寫這段代碼,它將會更快運行。我不熟悉symfony,但我假設你將數據存儲在數據庫中。在MySQL中,您可以使用命令INSERT ... ON DUPLICATE KEY update。如果您已將3個字段(代碼,名稱,姓氏)設置爲主鍵(我假設),則可以使用它來:插入數據,但如果鍵已經存在,則更新數據庫中的值。 MySQL會爲你做checsk,看看數據是否已經改變:如果沒有,就不會發生磁盤寫入。我相當肯定你可以編寫原生的SQL到symfony,允許你使用框架提供的安全性,但是加快你的插入速度。

+0

嗯,好吧,我想我只需要使用'set_time_limit',因爲超時大多是問題。 – Mentos93

+0

如果您不想更改時間限制,則可以創建腳本將行轉換爲單個SQL查詢。將查詢保存爲txt文件並將其上傳到服務器。使用php,您可以從文件中讀取X行,在數據庫上運行它們並保存最後一行。比下一次繼續從那一行。然而,以這種方式做更多的工作,但它會平衡更多的服務器負載。 – Jeffrey

2

通常,如果你想要性能,我最好的體驗是將所有數據轉儲到數據庫中,然後使用SQL語句在那裏轉換它。 DBS將能夠以這種方式優化您的所有步驟。

您可以直接導入CSV檔案到你的MySQL數據庫的SQL命令

LOAD DATA INFILE 'data.csv' 
INTO TABLE tmp_import 

該命令有很多,你可以指定你的CSV文件的格式選項,例如:

如果data.csv是包含所有舊行和新行的完整轉儲,那麼在修復它之後,您可以用導入的表替換當前表。

例如,它看起來像你的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, ... 

然後,您可以通過分組得到的不同行:

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

如果member_code是一個密鑰,你可以在MySQL中只需GROUP BY member_code。即使我相信這在技術上是against the standard,DBS也不會抱怨。

爲了讓您的數據,你做同樣的其餘部分:

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 

假設subj_codelec_code是主題和演講兩個鍵。

這個結果實際上保存爲可以使用MySQL的CREATE TABLE ... SELECT -syntax一個表,例如

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

然後,您可以做insertsupdates在兩個查詢:

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; 

和根據您的喜好選擇相同的主題和講座。

這一切就相當於您的CSV文件中,

  • 一個批量導入它應該是非常快的,
  • 3你的成員,主題和演講臨時表,
  • 3插入和3日更新聲明(每表)上的臨時表
  • 一滴表大功告成

後再次:如果你的C SV-File包含所有行,您可以替換現有表並保存3個插入和3個更新。

確保您在臨時表的相關列上創建索引,以便MySQL可以加快上述查詢中的NOT INJOIN

1

你可以先運行一個自定義的SQL從所有三個表

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中學說

Symfony2 & Doctrine: Create custom SQL-Query