5 октября 2008 г.

Киянкой по голове – лекарство от болезни роста

Окончание о болезнях роста баз данных. Как и предсказывали друзья, переход на InnoDB и шаманство с типами связующих полей двух больших таблиц в MySQL не дали сколько-нибудь приемлемого результата. После размышлений о непредсказуемом результате, смысле жизни, затратах на смену СУБД, от перевода всего на PostgreSQL решил отказаться.

Против лома нет приема и построчный перебор результатов запроса решил сделать обычными методами PHP, в обход Cake. Тоже, скажем прямо, не Cake way :-)

Первым делом поискал гугловской группе сообщения на аналогичную тему. Таки да, есть люди со схожими запросами. Более того, во глубине сибирских руд классов CakePHP есть метод fetchRow, но он неинтересен совершенно. При выполнении запроса Cake хранит ссылку на результат последнего запроса у себя в свойстве и перебирать построчно результаты можно, только если при этом не выполнять никаких других запросов. В противном случае результат запроса, естественно, будет перезаписан.

Поэтому задача перебора строк (это результат импорта данных) и назначения полям неких уникальных идентификаторов для связи с другой таблицей была решена в лоб:

// уже используемое Cake подключение к БД
$db = ConnectionManager::getDataSource($this->Model1->useDbConfig);
$conn = $db->connection;

//имя таблицы
$tabname = "{$this->Model1->tablePrefix}{$this->Model1->table}";

//Никаких вызовов contain и unbind :-)
//мы же в обход Cake действуем  
$res = mysql_query(
 "SELECT ... ", $conn);
    
 while (
 ( $row = mysql_fetch_array($res, MYSQL_ASSOC)) !== false 
 )
 {
  set_time_limit(0);
  
  //Это вызов компонента, он умеет делать
  //уникальные идентификаторы
  $uniqid = $this->SupplierCsv->suggestUniqId($row);
  
  //В связанной таблице Model2 есть такой ID?
  //если есть, то запишем его в Model1
  //иначе поле с уникальным идентификатором
  //будет равно NULL, и это будет означать
  //что мы сымпортировали что-то совсем неизвестное
  if ($this->Model1->Model2->hasAny(array('Model2.uniqid'=>$uniqid)))
  {

   // обновление записи в Model1
   // во-первых назначим значение для Model1->id
   // во вторых насильно укажем, что такая запись точно
   // существует. Это избавит Cake от желания
   // каждый раз выполнять запрос SELECT COUNT(*)
   // для выбора верной команды INSERT или UPDATE
   $this->Model1->id = $row['id'];
   $this->Model1->__exists = true;
    
   $this->Model1->save(array(
    'Model1'=>array(
    'model2_uniqid'=>$uniqid)));
   }
  }

Заодно обратите внимание, на насильное объяснение факта, что запись существует. Для этого достаточно присвоить значение с нужным id свойству $this->Model1->id и true свойству $this->Model1->__exists.

Весь этот код у меня пока в контроллере. Надо, наверное, его методом модели сделать.