2013-07-26 2 views
1

Я работаю в настоящее время над проектом аналитики и новичком по оптимизации запросов. Чтобы показать результат в браузере, требуется полная минут, в то время как только 45 000 записей должны быть доступны. Не могли бы вы предложить способы сократить время показа результатов.Django Query Optimization

Я написал следующий запрос, чтобы найти колл-длительность в лица возрастной группы:

sigma=0 
    popn=len(Demo.objects.filter(age_group=age)) 
    card_list=[Demo.objects.filter(age_group=age)[i].card_no 
       for i in range(popn)] 
    for card in card_list: 
     dic=Fact_table.objects.filter(card_no=card.aggregate(Sum('duration')) 
     sigma+=dic['duration__sum'] 
    avgDur=sigma/popn 

выше кода в цикл для итерации по возрастным группам.

Модель выглядит следующим образом:

class Demo(models.Model): 
    card_no=models.CharField(max_length=20,primary_key=True) 
    gender=models.IntegerField() 
    age=models.IntegerField() 
    age_group=models.IntegerField() 



class Fact_table(models.Model): 
    pri_key=models.BigIntegerField(primary_key=True) 
    card_no=models.CharField(max_length=20) 
    duration=models.IntegerField() 
    time_8bit=models.CharField(max_length=8) 
    time_of_day=models.IntegerField() 
    isBusinessHr=models.IntegerField() 
    Day_of_week=models.IntegerField() 
    Day=models.IntegerField() 

Благодарности

ответ

4

Try что:

sigma=0 
demo_by_age = Demo.objects.filter(age_group=age); 

popn=demo_by_age.count() #One 

card_list = demo_by_age.values_list('card_no', flat=True) # Two 

dic = Fact_table.objects.filter(card_no__in=card_list).aggregate(Sum('duration') #Three 
sigma = dic['duration__sum'] 

avgDur=sigma/popn 
+1

+1 за ваше терпение. Я никогда не видел такого количества анти-шаблонов в нескольких строках кода OP. – danihp

+0

Если '# 2' будет:' card_list = Demo.objects.filter (age_group = age) .values_list ('card_no', flat = True) '? И одна цитата отсутствует в 'sigma = dic ['duration__sum']' –

+0

Да, вы rigth – lalo

3

заявление как card_list=[Demo.objects.filter(age_group=age)[i].card_no for i in range(popn)] будет генерировать popn запросы и раздельные хиты базы данных. Запрос в for -loop также попадет в базу данных popn раз. Как правило, вы должны попытаться свести к минимуму количество запросов, которые вы используете, и вы должны выбрать только нужные вам записи.

С помощью нескольких настроек вашего кода это можно сделать только в одном запросе.

  • Там нет вообще никакой необходимости вручную указать primary_key, и во всех, кроме некоторых очень специфических случаях это даже лучше не определить какой-либо. Django автоматически добавляет индексированное, автоматически инкрементное поле первичного ключа. Если вам нужно card_no поля как уникальное поле, и вы должны найти строки на основе этой области, используйте:

    class Demo(models.Model): 
        card_no = models.SlugField(max_length=20, unique=True) 
        ... 
    

    SlugField автоматически добавляет индекс базы данных для столбца, по существу, делая выбор на этом поле так быстро как когда это первичный ключ. Это по-прежнему позволяет другим способам доступа к таблице, например. внешние ключи (как я объясню в следующем пункте), чтобы использовать (слегка) более быстрое целочисленное поле, указанное Django, и облегчит использование модели в Django.

  • Если вам нужно связать объект с объектом в другой таблице, используйте models.ForeignKey. Django предоставляет вам целый набор новых функций, которые не только упрощают использование моделей, но также ускоряют множество запросов с помощью предложений JOIN в SQL-запросе. Так что для вас, например:

    class Fact_table(models.Model): 
        card = models.ForeignKey(Demo, related_name='facts') 
        ... 
    

    Поля related_name позволяет получить доступ ко всем FACT_TABLE объекты, связанные с демо, например с помощью instance.facts в Django. (См https://docs.djangoproject.com/en/dev/ref/models/fields/#module-django.db.models.fields.related)

С помощью этих двух изменений, ваш запрос (включая петлю над различными age_groups) может быть изменен в пылающий-быстрый один-хит запроса дает вам среднюю продолжительность вызовов, сделанных каждым age_group :

age_groups = Demo.objects.values('age_group').annotate(duration_avg=Avg('facts__duration')) 
for group in age_groups: 
    print "Age group: %s - Average duration: %s" % group['age_group'], group['duration_avg'] 

.values('age_group') выбирает только поле age_group из таблицы базы данных в демо..annotate(duration_avg=Avg('facts__duration')) принимает каждый уникальный результат от values (таким образом, каждая уникальная age_group), и для каждого уникального результата будут извлекать все объекты Fact_table, связанные с любым демо-объектом в этой возрастной группе, и вычислять среднее значение всех полей продолжительности - все в одном запросе.

+0

Я отредактировал модели, а также таблицу mysql соответственно, но в duration_avg добавлено только значение «Нет». На самом деле «Нет» происходит только в одном случае, но не в других. Это так, если «Нет» сталкивается с аннотатом, не работает и почему это повлияло на другую age_group ?? Во всяком случае, стратегия оптимизации была довольно приятной. – Tisal

+0

@Bishal Это странно, потому что быстрый тест с моим собственным проектом показывает, что результат «Нет» не должен влиять на другие результаты. Можете ли вы обновить свой код в вопросе? – knbk