#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»».