Извлечь все строки из таблицы в доктрине

У меня есть таблица с 100 000+ строк, и я хочу выделить все это в доктрине и сделать некоторые действия с каждой строкой, в symfony2 с доктриной, я пытаюсь сделать с этим запросом:

    $query = $this->getDefaultEntityManager()
->getRepository('AppBundle:Contractor')
->createQueryBuilder('c')
->getQuery()->iterate();

foreach ($query as $contractor) {
// doing something
}

но затем я получаю утечку памяти, потому что я думаю, что он записал все данные в память.

У меня больше опыта в ADOdb, в этой библиотеке, когда я делаю так:

$result = $ADOdbObject->Execute('SELECT * FROM contractors');
while ($arrRow = $result->fetchRow()) {
// do some action
}

Я не получаю утечки памяти.

Так как же выбрать все данные из таблицы и не получить утечку памяти с доктриной в symfony2?

Вопрос РЕДАКТИРОВАТЬ

Когда я пытаюсь удалить foreach и просто выполнить итерацию, я также получаю утечку памяти:

$query = $this->getDefaultEntityManager()
->getRepository('AppBundle:Contractor')
->createQueryBuilder('c')
->getQuery()->iterate();

5

Решение

Нормальный подход заключается в использовании итерация ().

$q = $this->getDefaultEntityManager()->createQuery('select u from AppBundle:Contractor c');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
// do something
}

Однако, как говорится в документации доктрины, это может привести к ошибкам.

Результаты могут быть полностью буферизованы клиентом / соединением базы данных, выделяя дополнительную память, невидимую для процесса PHP. Для больших наборов это может легко убить процесс без видимой причины.

Самый простой подход к этому — просто создавать меньшие запросы со смещениями и ограничениями.

//get the count of the whole query first
$qb = $this->getDefaultEntityManager();
$qb->select('COUNT(u)')->from('AppBundle:Contractor', 'c');
$count = $qb->getQuery()->getSingleScalarResult();

//lets say we go in steps of 1000 to have no memory leak
$limit = 1000;
$offset = 0;

//loop every 1000 > create a query > loop the result > repeat
while ($offset < $count){
$qb->select('u')
->from('AppBundle:Contractor', 'c')
->setMaxResults($limit)
->setFirstResult($offset);
$result = $qb->getQuery()->getResult();
foreach ($result as $contractor) {
// do something
}
$offset += $limit;
}

С этими тяжелыми наборами данных это, скорее всего, будет проходить через максимальное время исполнения, который 30 секунд по умолчанию. Так что не забудьте вручную изменить set_time_limit в вашем php.ini. Если вы просто хотите обновить все наборы данных с помощью известного шаблона, вам следует рассмотреть возможность написания одного большого запроса на обновление, а не зацикливание и редактирование результата в PHP.

6

Другие решения

Попробуйте использовать этот подход:

foreach ($query as $contractor) {
// doing something

$this->getDefaultEntityManager()->detach($contractor);
$this->getDefaultEntityManager()->clear($contractor);
unset($contractor); // tell to the gc the object is not in use anymore

}

Надеюсь это поможет

0

Если вам действительно нужно получить все записи, я бы посоветовал вам использовать database_connection напрямую. Посмотрите на его интерфейс и выберите метод, который не будет загружать все данные в память (и не будет сопоставлять записи с вашей сущностью).

Вы можете использовать что-то вроде этого (предполагая, что этот код находится в контроллере):

$db = $this->get('database_connection');
$query = 'select * from <your_table>';
$sth = $db->prepare($query);
$sth->execute();
while($row = $sth->fetch()) {
// some stuff
}

Возможно, это не то, что вам нужно, потому что вы можете захотеть иметь объекты после обработки всей коллекции. Но, может быть, вам не нужны объекты. В любом случае подумай об этом.

0