Cómo escribir de manera eficiente DISTINTA de la consulta en Django con la tabla con claves externas

StackOverflow https://stackoverflow.com//questions/20013116

Pregunta

Quiero mostrar las diferentes ciudades de Usuarios en el extremo delantero de la lista desplegable.Por eso, yo hago un db de consulta que recupera los distintos city_name de la tabla City pero sólo aquellas ciudades donde los usuarios están presentes.

Algo así como obras de abajo para un tamaño pequeño de User mesa, pero toma un tiempo muy largo si User tabla de tamaño de 10 millones de dólares.Distintas ciudades de estos usuarios son todavía ~100 aunque.

class City(models.Model):
    city_code = models.IntegerField(unique=True)
    city_name = models.CharField(max_length=256)

class User(models.Model):
    city = models.ForeignKey('City', to_field='city_code')

Ahora trato de buscar distintos nombres de la ciudad como:

City.objects.filter().values_list('city__city_name').distinct()

lo que se traduce esto en PostgreSQL:

SELECT DISTINCT "city"."city_name" 
FROM "user" 
LEFT OUTER JOIN "city" 
                ON ("user"."city_id" = "city"."city_code");

Tiempo:9760.302 ms

Que mostraron claramente que PostgreSQL no estaba haciendo uso de índice en 'usuario'.'city_id'.Yo también he leído acerca de una solución aquí que implica escribir una consulta SQL personalizada que de alguna manera se utiliza el índice.

Traté de encontrar distintos 'usuario'.'city_id' mediante la consulta anterior, y que, en realidad, resultó ser bastante rápido.

WITH 
    RECURSIVE t(n) AS 
                     (SELECT min(city_id) 
                      FROM user 
                      UNION 
                      SELECT 
                            (SELECT city_id 
                             FROM user 
                             WHERE city_id > n order by city_id limit 1) 
                      FROM t 
                      WHERE n is not null) 
                      SELECT n 
                      FROM t;

Tiempo:79.056 ms

Pero ahora me estoy encontrando difícil de incorporar esto en mi Django código.Todavía creo que es un tipo de hack agregar consulta personalizada en el código de este.Pero una preocupación más grande para mí es que el nombre de la columna puede ser totalmente dinámico, y no puedo codificar estos nombres de columna (por ejemplo.city_id, etc.) en el código.

#original_fields could be a list from input, like ['area_code__district_code__name']
dataset_klass.objects.filter().values_list(*original_fields).distinct()

El uso de la consulta personalizada necesitaría al menos dividir el campo nombre con '__' como delimitador y el proceso de la primera parte.Pero se ve como un mal hack para mí.

¿Cómo puedo mejorar esto?

PS.El City User ejemplo es mostrado sólo para explicar el escenario.La sintaxis podría no ser correcta.

¿Fue útil?

Solución

Finalmente llegué a esta solución.

from django.db import connection, transaction

original_field = 'city__city_name'
dataset_name = 'user'
dataset_klass = eval(camelize(dataset_name))

split_arr = original_field.split("__",1)
"""If a foreign key relation is present
"""
if len(split_arr) > 1:
    parent_field = dataset_klass._meta.get_field_by_name(split_arr[0])[0]
    cursor = connection.cursor()
    """This query will run fast only if parent_field is indexed (city_id)
    """
    cursor.execute('WITH RECURSIVE t(n) AS ( select min({0}) from {1} '
                   'union select (select {0} from {1} where {0} > n'
                   ' order by {0} limit 1) from t where n is not null) '
                   'select n from t;'.format(parent_field.get_attname_column()[1], dataset_name))
    """Create a list of all distinct city_id's"""
    distinct_values = [single[0] for single in cursor.fetchall()]
    """create a dict of foreign key field to the above list"""
    """to get the actual city_name's using _meta information"""
    filter_dict = {parent_field.rel.field_name+'__in':distinct_values}
    values = parent_field.rel.to.objects.filter(**filter_dict).values_list(split_arr[1])
else:
    values = dataset_klass.objects.filter().values_list(original_field).distinct()

Que utiliza el índice de city_id en user mesa, corre bastante rápido.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top