2017-08-18 61 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