我有一个导入CSV文件的脚本。最终在我的数据库中的是客户列表和地址列表。我有一个名为customer
的表和另一个名为address
的表,其中address
有一个customer_id
。
对我来说重要的一件事就是不要有任何重复的行。因此,每次导入地址时,我都会这样做:
$address = new Address();
$address->setLine_1($line_1);
$address->setZip($zip);
$address->setCountry($usa);
$address->setCity($city);
$address->setState($state);
$address = Doctrine::getTable('Address')->findOrCreate($address);
$address->save();
正如您可能猜到的那样,findOrCreate()
找到匹配的地址记录(如果存在),否则只返回一个新的Address
对象。这是代码:
public function findOrCreate($address)
{
$q = Doctrine_Query::create()
->select('a.*')
->from('Address a')
->where('a.line_1 = ?', $address->getLine_1())
->andWhere('a.line_2 = ?', $address->getLine_2())
->andWhere('a.country_id = ?', $address->getCountryId())
->andWhere('a.city = ?', $address->getCity())
->andWhere('a.state_id = ?', $address->getStateId())
->andWhere('a.zip = ?', $address->getZip());
$existing_address = $q->fetchOne();
if ($existing_address)
{
return $existing_address;
}
else
{
return $address;
}
}
这样做的问题是它很慢。要保存CSV文件中的每一行(在不同的表中转换为多个INSERT
语句),大约需要四分之一秒。我希望尽可能接近“瞬时”,因为我的CSV文件中有时会有超过50,000行。我发现,如果我注释掉导入中保存地址的部分,那就快得多了。有没有更快的方法可以做到这一点?我简单地考虑过对其进行索引,但似乎是因为所有字段都需要匹配,所以索引无济于事。
答案 0 :(得分:1)
我建议您调查使用LOAD DATA INFILE
http://dev.mysql.com/doc/refman/5.1/en/load-data.html
为了更新现有行,您有几个选项。 LOAD DATA INFILE
没有upsert功能(在重复键更新时插入...),但它有一个REPLACE
选项,您可以使用它来更新现有行,但是您需要确保拥有一个合适的唯一索引,而REPLACE实际上只是DELETE
和INSERT
,它比UPDATE
慢。
另一种选择是将CSV中的数据加载到临时表中,然后使用INSERT...ON DUPLICATE KEY UPDAT
E将该表与实时表合并。同样,请确保您有一个合适的唯一索引,但在这种情况下,您正在进行更新而不是删除,因此它应该更快。
答案 1 :(得分:1)
这肯定不会减少花费在数万次迭代上的所有时间,但为什么不在每次迭代数据库查询之外管理你的地址呢?总体思路:
除非我误解了这个场景,否则你只需要进行INSERT查询,除了第一个查询之外你不需要执行任何SELECT查询。
答案 2 :(得分:0)
看起来你的重复检查会减慢你的速度。要找出原因,请找出Doctrine正在创建的查询,并在其上运行EXPLAIN
。
我的猜测是你需要创建一些索引。搜索整个表可能会非常慢,但是为zip添加索引将允许查询仅通过具有该邮政编码的地址进行完整搜索。 EXPLAIN
将能够引导您进行其他优化。
答案 3 :(得分:0)
我最终做的是,大大提高了效果,就是使用ON DUPLICATE KEY UPDATE而不是findOrCreate()
。