[pyar] [django] Consulta en tabla grande con annotate y Sum

Matias Varela matu.varela en gmail.com
Mie Dic 17 14:42:40 ART 2014


Gracias fisa!

El 17/12/14 12:49, fisa escribió:
> Vas a usar los otros campos? porque por SQL (y no por el ORM de
> django), necesitás incluir en el group by cualquier cosa que después
> vayas a necesitar leer en los resultados de la query.
No se usarán todos, pero si unos 5 o 6 campos de la entidad.
>
> Si solo vas a leer pocos campos y al resto no los necesitás, lo
> primero que se me ocurrió fue usar only o defer, de esta forma
> (imaginando que solo te interesa nombre y apellido):
>
> Cliente.objects.only('nombre',
> 'apellido').annotate(saldo=Sum('movimientos__monto'))
> o 
> Cliente.objects.defer('nombre',
> 'apellido').annotate(saldo=Sum('movimientos__monto'))
>
> Pero resulta que el ORM sigue metiendo los campos que no necesita en
> el group by, por más que no los devuelva. Esto está mal, se podría
> levantar un issue en el issue tracker de django para que lo mejoren.
>
> Pero buenas noticias! lo siguiente que intenté sí funciona
> correctamente: usar values. Lo malo es que a diferencia de only y
> defer (donde el ORM te sigue devolviendo instancias de tu modelo), con
> values vas a obtener una lista de diccionarios. Con values lo harías
> de esta forma:
>
> Cliente.objects.values('nombre',
> 'apellido').annotate(saldo=Sum('movimientos__monto'))
No habría problema con usar values, ya que al asignarlo a self.queryset,
django-tables2 lo toma bien. Pero el problema persiste, ya que el group
by lo hace por los campos del values:

2014-12-17 14:28:13 ART LOG:  duración: 29520.489 ms  sentencia: SELECT
"core_cliente"."nombre", "core_cliente"."apellido",
SUM("core_movimientocliente"."monto") AS "saldo" FROM "core_cliente"
LEFT OUTER JOIN "core_movimientocliente" ON ( "core_cliente"."id" =
"core_movimientocliente"."cliente_id" ) GROUP BY
"core_cliente"."nombre", "core_cliente"."apellido"

>
> Pero más buenas noticias! Como eso genera una query que el ORM
> tranquilamente puede usar en raw, lo que finalmente podés hacer para
> tener la chancha y los 20 (o sea, hacer la query óptima, y que te
> devuelva instancias de Movimiento), es esto:
>
> Cliente.objects.raw(str(Cliente.objects.values('nombre',
> 'apellido').annotate(saldo=Sum('movimientos__monto')).query))
>
Esto también lo intenté, pero la consulta sigue sin ser la óptima.

Sumado a esto, raw devuelve un RawQuerySet y falla al renderizar las
tablas ya que espera un QuerySet o un set con el método all().
Para solucionar esto, encontré por ahí un método que toma un raw y te
devuelve un QuerySet (dentro del Manager del modelo):

def raw_as_qs(self, raw_query, params=()):
        """Execute a raw query and return a QuerySet. The first column
in the
        result set must be the id field for the model.
        :type raw_query: str | unicode
        :type params: tuple[T] | dict[str | unicode, T]
        :rtype: django.db.models.query.QuerySet
        """
        cursor = connection.cursor()
        try:
            cursor.execute(raw_query, params)
            return self.filter(id__in=(x[0] for x in cursor))
        finally:
            cursor.close()

Pero pierdo el saldo, ya que es un campo calculado. fuck!


> No es super elegante, pero hace las dos cosas, optimiza la query y
> devuelve objetos Movimiento.
>
> Saludos!
La realidad es que ORM no demora demasiado en comparación con la query
en la db, el problema viene porque la query no está bien.

Esta tarda en el orden del segundo:

select c.id, c.nombre, c.apellido, (select sum(monto) as saldo from
core_movimientocliente where cliente_id = c.id)
from core_cliente c  #sin group by

¿Se puede generar un QuerySet manualmente? por lo que leí no. Porque
sería una solución.

¿Es posible convertir un RawQuerySet en QuerySet o ValuesQuerySet? esa
sería otra solución.

Ufff.. Muchas gracias por la ayuda!


-- 
*Matías E. Varela*
Skype: matu.varela
Jadder: matuu en python.org.ar

------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.python.org.ar/pipermail/pyar/attachments/20141217/c3487b1e/attachment-0001.html>


More information about the pyar mailing list