Как обращаться с большими доктринами Коллекций и Ассоциаций - PullRequest
8 голосов
/ 28 сентября 2011

Я работаю над проектом, в котором мне нужно собрать много данных из базы данных. Я использую Symfony2 (symfony bf1281aebdc842a39ec0eb7438e1ea3fca9b9705) и Doctrine2 (доктрина 3b3186ee98392802a44118cd421a3530119aa7eaand) в качестве рабочей базы.

Проблема, с которой я сталкиваюсь, заключается в том, что мне нужно получить около 15.000 статей. После этого мне нужно перебрать все из них, чтобы получить больше данных на основе идентификатора статьи (есть прямые и косвенные ассоциации с (т.е. унаследованными) носителями или ценами и т. Д.). Это нормально для примерно 50-100 записей, но если я хочу использовать больше записей, потребуется много оперативной памяти, чтобы извлечь все из базы данных.

Есть ли способ перебирать данные, не используя все оставшиеся оперативные памяти? Есть ли способ сказать доктрине, чтобы перестать использовать ссылки?

Заранее благодарю за любую помощь!

Ответы [ 5 ]

6 голосов
/ 25 сентября 2013

сохранить другую строку, используя:

$iterableResult = $doctrine->getManager()->createQuery("SELECT c FROM ENTITY c")->iterate();

while ((list($obj) = $iterableResult->next()) !== false) {
    // do something with $obj
    $em->detach($obj);
}
3 голосов
/ 24 января 2012

Вы можете изучить использование итеративной (пошаговой) гидратации в Doctrine:

$em = $this->getDoctrine()->getEntityManager();
$q  = $em->createQuery("<DQL to select the objects I want>");
$iterableResult = $q->iterate();
while (($row = $iterableResult->next()) !== false) {
    // do stuff with the data in the row, $row[0] is always the object
    $em->detach($row[0]); // detach from Doctrine, so that it can be GC'd immediately
}

Подробности из этой статьи , в разделе Массовая обработка объектов

3 голосов
/ 29 сентября 2011

Трудно ответить, не уточняя, что именно вы пытаетесь сделать.

Доктрина, как правило, не подходит для таких важных данных, как вы могли бы описать. У меня есть несколько проектов, в которых используется только DBAL для запуска необработанного SQL для таких вещей, как сложные отчеты.

Тем не менее, если вам на самом деле не нужно загружать все записи по 15k одновременно, не загружайте их сразу! Возьмите 50, обработайте их, освободите память и повторите. Этот подход может сохранять итоги по мере продвижения, поэтому можно получить некоторую статистическую статистику для всего набора.

Тем не менее, если вам нужны агрегированные данные, вы, вероятно, создаете какой-то «отчет», и вам может быть лучше использовать чистый SQL или даже некоторые хранимые процедуры, и не допускать ORM принять участие.

1 голос
/ 13 июля 2013

На самом деле есть другой путь, но он вам не понравится. Вы можете использовать чистый MySQL :) Когда я пытался получить 450 тыс. Объектов из базы данных, используя getRepository ("..") -> findAll () - это заняло несколько часов, пока я не закрыл скрипт :). Так что я использовал код как

    $c = $doctrine->getConnection();
    mysql_connect($c->getParams()['host'],$c->getParams()['user'],$c->getParams()['password']);//$link =
    mysql_select_db($c->getParams()['dbname']);
    $qid = mysql_query("SELECT id FROM TABLENAME");
    while($i = mysql_fetch_row($qid)) {
        $object = $doctrine->getRepository("...")->find($i[0]);

Я получил первый объект через 1,7 секунды , что для меня абсолютно приемлемо. Кроме грязного кода: (

После того, как я немного погуглил, я нашел вопрос и Руководство по доктрине . После переписывания я получил код

    $iterableResult = $doctrine->getManager()->createQuery("SELECT c FROM ENTITY c")->iterate();

    while (($row = $iterableResult->next()) !== false) {
        $object = $row[0];
    }

Код, приведенный выше, предоставил первый элемент за 2,4 секунды , что очень быстро, как для элементов doctrine2 и 450k в таблице.

Суть в том, что в doctrine2 уже есть почти все, что вам нужно для «реального» использования, но вы все равно можете упростить свою жизнь с помощью некрасивого кода старой школы:)

ОБНОВЛЕНИЕ: не забудьте каскадно отсоединить все загруженные объекты. А в symfony2 вам также нужно отключить ведение журнала SQL, запустив код

    $doctrine->getConnection()->getConfiguration()->setSQLLogger(null);
0 голосов
/ 18 марта 2015

Из доктрины Документация :

$batchSize = 20;
$i = 0;
$q = $em->createQuery('select u from MyProject\Model\User u');
$iterableResult = $q->iterate();
foreach ($iterableResult as $row) {
    $user = $row[0];
    $user->increaseCredit();
    $user->calculateNewBonuses();
    if (($i % $batchSize) === 0) {
        $em->flush(); // Executes all updates.
        $em->clear(); // Detaches all objects from Doctrine!
    }
    ++$i;
}
$em->flush();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...