我从第三方那里得到了一些记录(大约30k),结构类似于这个(所有字符串):
first_name, last_name, city
另外,我在MySQL中有两个表:users和cities(大约25k条记录)。用户表具有cities表的外键。
我需要用api和foregin键到users表的记录填充citites表。如果城市不存在,我需要创造它。所以我的代码如下:

<?php

$users = $api->getUsers();

$existingUsers = $this->userRepository->getIds();
$existingCities = $this->cityRepository->geIdsIndexedByName();

$db->beginTransaction();

foreach ($users as $i => $user) {
  // if no city with such name, then create new and insert to array
  if (!array_key_exists($user['city'], $existingCities) {
    $cityId = $db->insert('cities', ['name' => $user['city']]);
    $existingCities[$user['city_id']] = $cityId;
  }
  $user['city_id'] = $existingCities[$user['city']];

  if (in_array($user['id'], $existingUsers) { // if record with such id exists, then we update it
      $db->update('users', $user);
  } else {
      $db->insert('users', $user);
  }
  if (($i % 100) === 0) { // use transactions to avoid mass inserts and updates
    $db->commit();
    $db->beginTransaction();
  }
}

我不喜欢的是,我必须将所有的城市和所有用户加载到内存中检查记录是否已经存在。我在这里不使用ORM,没有对象,只有很小的数组,但它仍然消耗资源,我想减少内存消耗。有没有什么实践可以优化这个过程?
我有一个想法,使用NoSQL存储作为现有用户和CITYTES的缓存,但它是不允许的。

最佳答案

如果我理解正确的话,为了把新名单和旧名单匹配起来,你必须在城市名称上匹配。虽然这并不理想,但可能是唯一可行的解决方案。
如果你的城市名是唯一的,没问题。但如果它们不是唯一的(Springfield AK、Springfield CA、Springfield CO、Springfield GA、Springfield ID、Springfield IL、Springfield IN等),则需要更多信息来匹配。此外,拼写错误(springfield vs springfield)会破坏您规范化数据库的原因。。。
第一步,将新信息添加到新表中。我称之为新信息。。。

newinfo
----------
first_name
last_name
city

因此,假设城市名称是唯一的,下一步是将所有新的唯一城市名称添加到cities。如果您在cities.name上有一个唯一的索引,您可以
insert ignore into cities('name') select city from newinfo

否则,必须联接表以查找新值:
insert into cities ('name')
select newinfo.city
from newinfo
left join cities on newinfo.city=cities.name
where cities.name is null

既然您的cities表中包含了所有可能的城市,那么您需要插入所有新名称。考虑如何获取需要插入的信息:
select newinfo.first_name, new info.last_name, cities.id
from newinfo
inner join cities on new info.city=cities.name

然后,当您确信获得了正确的数据时,请插入:
insert into users
values('first_name','last_name','city_id')
select newinfo.first_name, new info.last_name, cities.id
from newinfo
inner join cities on new info.city=cities.name

现在您已经完成了newinfo表的处理,它可以被删除。

09-19 19:21