Оптимизация foreach для тысяч элементов

#php #memory #optimization #yii #foreach

#php #память #оптимизация #yii #foreach

Вопрос:

Я запускаю приведенный ниже код над набором из 25 000 результатов. Мне нужно оптимизировать его, потому что я достигаю предела памяти.

 $oldproducts = Oldproduct::model()->findAll(); /*(here i have 25,000 results)*/

foreach($oldproducts as $oldproduct) :
    $criteria = new CDbCriteria;
    $criteria->compare('`someid`', $oldproduct->someid);
    $finds = Newproduct::model()->findAll($criteria);

    if (empty($finds)) {
        $new = new Newproduct;
        $new->someid = $oldproduct->someid;
        $new->save();
    } else {
        foreach($finds as $find) :
            if ($find->price != $oldproduct->price) {
                $find->attributes=array('price' => $oldproduct->price);
                $find->save();
            }
        endforeach;
    }
endforeach;
  

Код сравнивает строки двух таблиц по некоторому идентификатору. Если он находит совпадение, он обновляет столбец цены, если нет, создает новую запись.

Комментарии:

1. В какой строке вы обнаруживаете ошибку? Почему вы не можете просто сделать это с помощью SQL-запроса? Это займет некоторое время, но проходите через пакеты по 100 за раз, а не извлекайте все 25 тыс. (являющийся внешним циклом for ($offset = 0; $offset < 25000; $offset = 100) ).

2. Этот вопрос кажется не по теме, потому что он относится к проверке кода , пока код работает, и вопрос соответствует их рекомендациям.

3. Вы используете ActiveRecords для огромного объема данных, что определенно является «НЕТ-НЕТ». Старайтесь избегать ActiveRecords в подобных ситуациях.

4. Я не могу использовать SQL, потому что этот код должен проверять наличие новых записей в таблице Oldproduct нажатием кнопки. Он будет часто использоваться

Ответ №1:

Используйте CDataProviderIterator , который:

… позволяет выполнять итерации по большим наборам данных, не удерживая весь набор в памяти.

Сначала вам нужно передать CDataProvider ему экземпляр:

 $dataProvider = new CActiveDataProvider("Oldproduct");
$iterator = new CDataProviderIterator($dataProvider);
foreach($iterator as $item) {
    // do stuff
}
  

Ответ №2:

Вы могли бы обрабатывать строки порциями по ~ 5000 вместо того, чтобы получать все строки за один раз!

 $cnt = 5000;
$offset = 0;

do {
    $oldproducts = Oldproduct::model()->limit($cnt)->offset($offset)->findAll(); /*(here i have 25,000 results)*/

    foreach($oldproducts as $oldproduct) {
        // your code
    }

    $offset  = $cnt;
} while($oldproducts >= $cnt);
  

Комментарии:

1. Спасибо за ответ, но есть ошибка — «нет метода или замыкания с именем «limit»».