martes, 16 de octubre de 2007

Optimizando consultas en el modelo.

Utilizar un ORM tienes sus ventajas, pero también sus desventajas. Manejar el acceso a registros específicos de la base de datos para hacer modificaciones o consultas resulta muy cómodo utilizando objetos, y la penalización en el rendimiento es prácticamente imperceptible. Pero que pasa cuando necesitamos realizar una consulta que involucra campos de varias tablas, y la cantidad de registros a recuperar son cientos o quizás miles, como por ejemplo, la consulta para un reporte. Es viable realizarlo creando un método que cree objetos "enlazados", similar a como lo hace los métodos doSelectJoinXXXX de las clases peer de los modelos base, pero si la cantidad de registros es muy grande los tiempos de respuestas serán demasiado altos, y probablemente la aplicación se quede sin memoria, dependiendo la configuración memory_limit de PHP.
Para solventar este escollo es preferible crear un método en el modelo que genere en vez de un arreglo de objetos, un arreglo de hashes, en el cual solo se retornen los valores que nos interesan.
Volvamos al ejemplo con que trabajamos en el post anterior. Supongamos que queremos crear, en nuestro módulo personas, un reporte de tipo listado, donde aparezca el nombre de la persona, el país, estado y municipio donde reside. En la mayoría de las recomendaciones que encontramos en internet con respecto a estos casos indican que debemos crear método donde se defina el query con un string, parsearle los campos, y ejecutarlo. Veamos un ejemplo para entenderlo mejor:

<?php
/**
* Subclass for performing query and update operations on the 'persona' table.
*
*
*
* @package lib.model
*/

class PersonaPeer extends BasePersonaPeer
{
public static function getList()
{
$personas = array();
$con = Propel::getConnection();
$query = 'SELECT %s , %s, %s,
%s, %s
FROM %s
left join %s on %s = %s
left join %s on %s = %s
left join %s on %s = %s'
;

$query = sprintf($query, self::ID_PERSONA , self::NOM_PERSONA ,
PaisPeer::NOM_PAIS , EstadoPeer::NOM_ESTADO , MunicipioPeer::NOM_MUNICIPIO ,
self::TABLE_NAME,
PaisPeer::TABLE_NAME ,self::ID_PAIS , PaisPeer::ID_PAIS ,
EstadoPeer::TABLE_NAME , self::ID_ESTADO , EstadoPeer::ID_ESTADO ,
MunicipioPeer::TABLE_NAME , self::ID_MUNICIPIO , MunicipioPeer::ID_MUNICIPIO );

$stmt = $con->prepareStatement($query);
$rs = $stmt->executeQuery();
while ($rs->next())
{
$persona['id'] = $rs->getInt(1);
$persona['nombre'] = $rs->getString(2);
$persona['pais'] = $rs->getString(3);
$persona['estado'] = $rs->getString(4);
$persona['municipio'] = $rs->getString(5);
$personas[] = $persona;
}
return $personas;
}
}

Como vemos, este método del peer retorna un arreglo de hashes, cuya busqueda y armado es más rápido que armar un arreglo de objetos. Pero, que desventajas tiene?. La principal desventaja es que, a diferencias de otros métodos del peer, esto no nos permite pasarle condiciones de filtrado u ordenamiento a través de criteria. Para solventar esa deficiencia vamos a reescribir el mismo pero usando un objeto criteria, que se recibirá como parámetro:

<?php
/**
* Subclass for performing query and update operations on the 'persona' table.
*
*
*
* @package lib.model
*/

class PersonaPeer extends BasePersonaPeer
{
public static function getList(Criteria $criteria)
{
$personas = array();
// Clonamos el objeto, para evitar modificar el objeto original
$criteria = clone $criteria;
// Eliminanos las columnas de selección en caso de que esten definidas
$criteria->clearSelectColumns();
// Agregamos las columnas de las tablas que queremos recuperar
$criteria->addSelectColumn(self::ID_PERSONA );
$criteria->addSelectColumn(self::NOM_PERSONA );
$criteria->addSelectColumn(PaisPeer::NOM_PAIS );
$criteria->addSelectColumn(EstadoPeer::NOM_ESTADO );
$criteria->addSelectColumn(MunicipioPeer::NOM_MUNICIPIO );
// Agregamos los Joins entre las distintas tablas
$criteria->addJoin(self::ID_PAIS ,PaisPeer::ID_PAIS,Criteria::LEFT_JOIN );
$criteria->addJoin(self::ID_ESTADO , EstadoPeer::ID_ESTADO,Criteria::LEFT_JOIN );
$criteria->addJoin(self::ID_MUNICIPIO, MunicipioPeer::ID_MUNICIPIO );
//Recuperamos los registros y generamos el arreglo de hashes
$rs = self::doSelectRS($criteria);
while ($rs->next())
{
$persona['id'] = $rs->getInt(1);
$persona['nombre'] = $rs->getString(2);
$persona['pais'] = $rs->getString(3);
$persona['estado'] = $rs->getString(4);
$persona['municipio'] = $rs->getString(5);
$personas[] = $persona;
}
return $personas;
}
}


Aunque fue un poco más difícil de escribir, este método hace el mismo trabajo que el anterior, con la ventaja que recibe un objeto criteria, con lo cual podemos definir filtros por cualquier campo de las 4 tablas involucradas en la consulta ( Ejem: Obtener los datos de personas que viven en un estado específico, entre otros), lo cual hace este método más reusable. En un próximo post veremos como podemos utilizar este mismo método en la acción list autogenerada de nuestro módulo persona.

22 comentarios:

Anónimo dijo...

Gracias por invertir tiempo en escribir estos artículos. A algunos nos son útiles ;-)

Boris Duin dijo...

Amigo, gracias por tu comentario. Espero poder seguir aportando cosas útiles sobre nuetro framework favorito ;)

Yusniel Hidalgo dijo...

Muchas felicidades Boris. Tienes un excelente blog.

Anónimo dijo...

Buenos Dias, me gustaria saber a que se debe este error:

[PropelException]
[wrapped: Could not execute query [Native Error: ERROR: no existe la columna descripcion.update_at LINE 1: ..., descripcion.CREATED_AT, descripcion.CREATED_BY, descripcio... ^] [User Info: SELECT descripcion.ID_PADRE, descripcion.NOM_DESCRIPCION, descripcion.CANT_HIJOS, descripcion.STATUS_REGISTRO, descripcion.ID_DESCRIPCION, descripcion.CREATED_AT, descripcion.CREATED_BY, descripcion.UPDATE_AT, descripcion.UPDATED_BY FROM descripcion LIMIT 20]]

tengo todo bien configurado y esta toda la base de datos en orden.

sgwd dijo...

Realmente muy bueno lo que has publicado. Felicitaciones!

Era justo lo que necesitaba ya que estoy comenzando mi primer proyecto con Symfony ("increible por cierto") y buscaba la manera de tener un mayor control sobre los objetos a seleccionar involucrando varias tablas.

Excelente material.

Voy a continuar leyendo los otros post.

Gracias!

Anónimo dijo...

Muchas gracias por este artículo.. es lo que estaba buscando..

se agradece de verdad ;)

Anónimo dijo...

generic viagra india get viagra buy viagra meds online viagra online no prescription cialis levia and viagra viagra over the counter viagra online uk viagra patent viagra online stores viagra soft tabs viagra uk cheap purchase buy buy viagra in england generic soft tab viagra ship free viagra sample

Anónimo dijo...

hot lesbians gallery pictures claire sweeney lesbian affair with lesbian latino teen lesbians amateur lesbian first time video free membership lesbian websites alison krauss a lesbian

Anónimo dijo...

kate lesbian hot lesbian anime girls indian lesbian capital anime free lesbian porn videos hothouse blogging for lesbians german lesbian babes lesbian and straight women

Anónimo dijo...

Lovingly done is better than spectacularly said.

Anónimo dijo...

Artistically done is richer reconsider than extravagantly said.

Anónimo dijo...

Splendidly done is richer reconsider than extravagantly said.

Anónimo dijo...

Artistically done is sick than well said.

Anónimo dijo...

A humankind who dares to decay everyone hour of one of these days has not discovered the value of life.

[url=http://happytailsanimalcarecenter.webs.com/apps/profile/profilePage?id=53375468]Ana[/url]


Linsey

Anónimo dijo...

To be a noble benign being is to be enduring a make of openness to the in the seventh heaven, an cleverness to group unsure things beyond your own control, that can lead you to be shattered in unequivocally extreme circumstances pro which you were not to blame. That says something exceedingly impressive relating to the prerequisite of the righteous passion: that it is based on a trustworthiness in the uncertain and on a willingness to be exposed; it's based on being more like a shop than like a prize, something fairly dainty, but whose mere special attraction is inseparable from that fragility.

Anónimo dijo...

To be a upright charitable being is to have a kind of openness to the world, an skill to guardianship unsure things beyond your own pilot, that can take you to be shattered in hugely exceptionally circumstances as which you were not to blame. That says something exceedingly important relating to the fettle of the ethical autobiography: that it is based on a trustworthiness in the unpredictable and on a willingness to be exposed; it's based on being more like a plant than like a treasure, something kind of dainty, but whose very particular attraction is inseparable from that fragility.

Anónimo dijo...

Vex ferments the humors, casts them into their adapted channels, throws bad redundancies, and helps nature in those secret distributions, without which the fuselage cannot subsist in its vigor, nor the soul role of with cheerfulness.

Anónimo dijo...

In every tom's sustenance, at some occasion, our inner fire goes out. It is then burst into zeal beside an face with another magnanimous being. We should all be glad recompense those people who rekindle the inner inspiration

Anónimo dijo...

In the whole world's existence, at some time, our inner fire goes out. It is then blow up into enthusiasm by an contend with with another magnanimous being. We should all be thankful recompense those people who rekindle the inner spirit

Unknown dijo...

Extraordinary work! The content given was invaluable. I am hoping that you sustain the great work done.
fort worth locksmiths

Anónimo dijo...

big t need to drive to any of your buddy as you can prepare sufficient cash by picking any on the web entity? To get such people, no homeowner lending products are best appropriate loan choice. You can even see issues, if any, from other individuals borrowers who were not satisfied with their own services. same day loans In many cases, co-op college students are employed on seeing that full time staff following school.

Anónimo dijo...

As a way to deduct property finance loan interest that you have paid out over the year a number of conditions must be met. Because of this its worth is increasing each day. Most of these lenders offer $100 to $1500 payday cash loans for 8 to 21 days for a small fee of $10 to $30 for every single $100 you be lent for a full week. same day loans online You don't have to pay it back as fast if you utilize the right bank and the acceptance process is quite quick and easy.