2011-09-28 5 views
8

Estoy trabajando en un proyecto en el que tengo que recopilar una gran cantidad de datos de la base de datos. Estoy utilizando Symfony2 (symfony bf1281aebdc842a39ec0eb7438e1ea3fca9b9705) y Doctrine2 (doctrina 3b3186ee98392802a44118cd421a3530119aa7eaand) como base de trabajo.Cómo manejar una gran doctrina Colecciones y asociaciones

El problema que encuentro es que tengo que buscar unos 15.000 artículos. Después de eso, necesito iterar a través de todos ellos para obtener más datos en la base de la identificación del artículo (hay asociaciones directas e indirectas con medios o precios (heredados), etc ...). Está bien para aproximadamente 50-100 registros, pero si quiero usar más registros, se necesita mucho tiempo de memoria RAM para obtener todo de la base de datos.

¿Hay alguna manera de recorrer los datos sin utilizar todo el RAM restante? ¿Hay alguna manera de decirle a la doctrina que deje de usar referencias?

Gracias de antemano por cualquier ayuda!

Respuesta

3

Es difícil de responder sin más detalles acerca de lo que está tratando de hacer, exactamente.

En general, Doctrine no es una buena opción para los datos crujientes como usted podría estar describiendo. Tengo varios proyectos que recurren simplemente al uso de DBAL para ejecutar SQL sin procesar para cosas como informes complicados.

Dicho esto, si no necesita todos los 15k registros cargados a la vez, ¡entonces no los cargue de una vez! Coge 50, trátalos, libera la memoria y repite. Este enfoque puede mantener los totales sobre la marcha, por lo que es posible obtener algunas estadísticas agregadas para todo el conjunto.

Dicho esto, si necesita material agregado, probablemente esté creando algún tipo de "informe", y podría ser mejor que utilice SQL puro, o incluso algunos procedimientos almacenados, y no permita que el ORM se involucra.

+0

y me dice, exactamente lo Aplicación impulsada por Symfony podría necesitar 15k registros a la vez? Solo la minería de datos necesita esa cantidad de datos y eso no debe ser alimentado por Symfony. –

3

Usted puede mirar en el uso de la hidratación iterativa (paso a paso) en Doctrina:

$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 
} 

detalles de this article, bajo procesamiento de objetos de masa

+0

FYI: el enlace está roto – Tieme

1

En realidad, hay otra manera, pero no me gustará Puede usar pure mysql :) Cuando traté de obtener 450k objetos de la base de datos usando getRepository ("..") -> findAll() - tardó horas hasta que cerré el script :). Por lo que utiliza un código como

$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]); 

llegué primer objeto después de 1,7 segundos, lo cual es absolutamente aceptable para mí. Excepto código desordenado :(

Tras Google un poco localicé usted pregunta y doctrine manuall. Después de volver a escribir código llegué

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

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

Código anteriormente proporcionada primer elemento de 2,4 segundos, lo cual es extremadamente rápido, como para los artículos Doctrine2 y 450K en la tabla

Mi punto es -. Doctrine2 ya tienen casi todo lo necesario para "la vida real" uso, pero todavía se puede simplificar su vida con la vieja escuela código feo :)

ACTUALIZACIÓN: no te olvides de desconectar en cascada todas las entidades cargadas.Y en Symfony2 también es necesario deshabilitar el registro de SQL mediante la ejecución de código

$doctrine->getConnection()->getConfiguration()->setSQLLogger(null); 
6

guardar otra línea mediante el uso de:

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

while ((list($obj) = $iterableResult->next()) !== false) { 
    // do something with $obj 
    $em->detach($obj); 
} 
0

De doctrina documentation:

$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(); 
Cuestiones relacionadas