Как оптимизировать использование наборов запросов со списками - PullRequest
1 голос
/ 15 марта 2011

У меня есть модель, которая имеет пару миллионов объектов. Каждый объект представляет собой вызов, сделанный / полученный компанией.

Для упрощения, скажем, эта модель Call имеет следующие поля:
calldate, context, channel.

Моя цель - узнать среднее количество звонков, совершенных и полученных в течение каждого часа дня месяца (загрузка по часам). Подвох заключается в следующем: мне нужно найти это для port1 и port2 отдельно.

На данный момент мой код работает нормально, за исключением того, что для получения результата в диапазоне от 4 месяцев требуется около 1 минуты, а мне он кажется крайне неэффективным.

Я провел простое профилирование и обнаружил, что расширение занимает около 99% времени обработки:

queryset = Call.objects.filter(calldate__gte='SOME_DATE')
port1, port2 = [],[]
port1.extend(queryset.filter(context__icontains="e1-1"))
port2.extend(queryset.filter(context__icontains="e1-2"))
channels_in_port1 = ["Port/%d-2" % x for x in range(1,32)]
channels_in_port2 = ["Port/%d-2" % x for x in range(32,63)]

for i in channels_in_port1:
    port1.extend(queryset.filter(channel__icontains=i))
for i in channels_in_port2:
    port2.extend(queryset.filter(channel__icontains=i))

port1 и port2 объединяют около 150 тыс. Объектов.

Как только у меня будут все звонки на port1 и port2 , я готов идти. Остальная часть кода - это в основном циклы for для port1 и port2 , которые суммируют и принимают среднее количество вызовов в соответствии с часом / днем ​​/ месяцем. Тривиальные вещи.

Я пытался избежать использования какого-либо «расширения», используя itertools.chain и связывая вместо этого наборы запросов. Однако это привело к смещению времени обработки в ту часть, где я выполняю тривиальные циклы for, чтобы вычислить нагрузку по часам.

Есть альтернативы? Лучшие способы отфильтровать набор запросов?
Большое спасибо !!

Ответы [ 2 ]

0 голосов
/ 15 марта 2011

Полагаю, ваша проблема со вторым набором расширений, то есть с теми, которые находятся внутри циклов for, а не с первым.(Во всяком случае, первое совершенно не нужно: вместо того, чтобы определять пустой список заранее и расширять его, вы можете просто сделать port1 = list(queryset.filter(context__icontains="e1-1")).)

В любом случае, чтобы подвести итог того, что, по вашему мнению, вы пытаетесь сделать: вы хотите получить все Call объекты на определенную дату, в двух блоках в зависимости от значения для channel: один, где он содержит значения от 0 до 31, и один со значениями от 32 до 62.

Похоже, что вы могли бы сделать это всего двумя запросами без какого-либо расширения:

port1 = queryset.filter(channel__range=["Port/1-2", "Port/31-2"])
port2 = queryset.filter(channel__range=["Port/1-32", "Port/31-62"])

Разве это не делает то, что вы хотите?

Редактировать в ответ накомментарий но это всего лишь два запроса, которые вы можете расширить или объединить.Проблема с опубликованным кодом заключается в том, что вы выполняете 31 запросов и расширяете операции для каждого порта, что обойдется дорого.Если вы просто сделаете одно по одному, плюс одно расширение / конкат, это будет намного дешевле.

0 голосов
/ 15 марта 2011

Рассматривали ли вы использование агрегатных функций django? http://docs.djangoproject.com/en/dev/topics/db/aggregation/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...