python / django: вложенный цикл действительно медленный для обхода набора запросов - PullRequest
0 голосов
/ 08 мая 2018

У меня есть две модели с именами машина и производительность ,

class machine(models.Model):
    machine_type = models.CharField(null=True, max_length=10)
    machine_no = models.IntegerField(null=True)    
    machine_name = models.CharField(null=True,max_length=255)
    machine_sis = models.CharField(null=True, max_length=255)
    store_code = models.IntegerField(null=True)
    created = models.DateTimeField(auto_now_add=True)

class Performance(models.Model):
    machine_no = models.IntegerField(null=True)
    power = models.IntegerField(null=True)
    store_code = models.IntegerField(null=True)
    created = models.DateTimeField(auto_now_add=True)

Для каждой машины в Performance Model есть несколько полей , и я должен найти количество строк * Performance Model в базе данных, которые имеют мощность = Some_integer. Вот как выглядит мой взгляд:

machines = machine.objects.filter(machine_type="G",machine_sis="919")
# let's say machine.count() sometimes is 100
# for each of this machine i need to calculate the number of machines which have power = 100 in performance model. 
# so what i did first was but was really slow
for obj in machines:
  print performance.objects.filter(machine_no=obj.machine_no,power=100).count()
# my second approach was faster than first approach
for obj in machines:
    data = performance.objects.filter(machine_no=obj.machine_no,power=100)
    counter = 0
    for p in data: # ***** lets say this loop is called star-loop
        if p.power == 100:
            counter +=1

Моя проблема: Скорость действительно низкая, когда мне нужно проверить 100 машин в модели Performance, мощность которых = что-то.

Дополнительная информация: Я не использую внешний ключ в Performance Model , потому что фактическая архитектура более сложна, и я не могу использовать номер машины или что-либо еще в качестве внешнего ключа, потому что при уникальной идентификации каждой машины мне нужно несколько столбцов машины. Кроме того, этот проект работает в производстве, и я не могу рисковать. Я использую django 1.11, python 2.7 и postresql rds. Я увеличил производительность сети, купив лучший экземпляр от AWS. Также я использовал время

Ответы [ 3 ]

0 голосов
/ 08 мая 2018

Вы можете выполнять подсчет и фильтрацию на стороне Python:

from collections import Counter

c = Counter(performance.objects.filter(power=100).
            values_list('machine_no', flat=True))


m = machine.objects.filter(machine_type="G",machine_sis="919")
    .values_list('machine_no', flat=True)

result = sum(v for k,v in c.items() if k in m)

что делать, если мне нужно power = 100, а также отдельный список машин с мощность = 99? Должен ли я использовать две отдельные функции Counter () с запрос?

Нет, просто добавьте фильтр к тому же запросу, используя Q объект , а затем вычислите два разных результата, например:

from collections import Counter
from django.db.models import Q

c = Counter(performance.objects.filter(power=100 | Q(power=99)).
            values_list('machine_no', 'power'))

m = machine.objects.filter(machine_type="G",machine_sis="919")
    .values_list('machine_no', flat=True)

result_100 = sum(v for k,v in c.items() if k[0] in m and k[1] = 100)
result_99  = sum(v for k,v in c.items() if k[0] in m and k[1] = 99)
0 голосов
/ 08 мая 2018

Вы можете использовать необработанные запросы.Может быть, так.Пожалуйста, обновите, чтобы точно использовать имя таблицы базы данных.

 machine.objects.raw(select * from machine as b
      join (select count(id), machine_no from performance where power=100 
      group by machine_no) as a
      on b.id = a.machine_no
      where b.machine_type="G" and b.machine_sis="919")
0 голосов
/ 08 мая 2018

Это похоже на случай проблемы N + 1 Select. Вы можете сделать следующее, чтобы уменьшить количество запросов:

machines = machine.objects.filter(machine_type="G",machine_sis="919")
machine_nos = machine.values_list('machine_no', flat=True)
performance = performance.objects.filter(machine_no__in=machine_nos, power=100)

Это уменьшает количество запросов максимум до трех

...