Ускорение запроса Django - PullRequest
0 голосов
/ 20 января 2020

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

Вот факсимиле соответствующих моделей в нашей базе данных.

class Company(models.Model):
    name = models.CharField(max_length=255)

    @property
    def active_humans(self):
        if hasattr(self, '_active_humans'):
            return self._active_humans
        else:
            self._active_humans = Human.objects.filter(active=True, departments__company=self).distinct()
            return self._active_humans

class Department(models.Model):
    name = models.CharField(max_length=225)
    company = models.ForeignKey(
        'muh_project.Company',
        related_name="departments",
        on_delete=models.PROTECT
    )
    humans = models.ManyToManyField('muh_project.Human', through='muh_project.Job', related_name='departments')

class Job(models.Model):
    name = models.CharField(max_length=225)
    department = models.ForeignKey(
        'muh_project.Department',
        on_delete=models.PROTECT
    )
    human = models.ForeignKey(
        'muh_project.Human',
        on_delete=models.PROTECT
    )

class Human(models.Model):
    active = models.BooleanField(default=True)

    @property
    def fixed_happy_dogs(self):
        return self.solutions.filter(is_neutered_spayed=True, disposition="happy")

class Dog(models.Model):
    is_neutered_spayed = models.BooleanField(default=True)
    disposition = models.CharField(max_length=225)
    age = models.IntegerField()

    human = models.ForeignKey(
        'muh_project.Human',
        related_name="dogs",
        on_delete=models.PROTECT
    )
    human_job = models.ForeignKey(
        'muh_project.Job',
        blank=True,
        null=True,
        on_delete=models.PROTECT
    )

Что я пытаюсь сделать (на языке этого примера глупой игрушки) чтобы получить количество людей с по крайней мере одним из определенного типа собаки для каждой из некоторых компаний. Итак, что я делаю, так это запускаю.

rows = []
company_type = "Tech"
fixed_happy_dogs = Dog.objects.filter(is_neutered_spayed=True, disposition="happy")
old_dogs = fixed_happy_dogs.filter(age__gte=7)
companies = Company.objects.filter(name__icontains=company_type)
for company in companies.order_by('id'):
    humans = company.active_humans
    num_humans = humans.distinct().count()
    humans_with_fixed_happy_dogs = humans.filter(dogs__in=fixed_happy_dogs).distinct().count()
    humans_with_old_dogs = humans.filter(dogs__in=old_dogs).distinct().count()
    rows.append(f'{company.id};{num_humans};{humans_with_fixed_happy_dogs};{humans_with_old_dogs}')

Обычно запуск занимает от 45 до 120 секунд в зависимости от того, сколько компаний я им управляю. Я хотел бы сократить это. Мне нужен конечный результат в виде списка строк, как показано.

1 Ответ

2 голосов
/ 20 января 2020

Один низко висящий фрукт - это добавить индекс db в столбец Dog.disposition, поскольку он используется в операторе .filter(), и похоже, что он должен выполнять сканирование последовательности по таблице (каждый раз, когда через for l oop).

Специально для этой задачи я бы рекомендовал использовать Django Панель инструментов отладки , где вы можете увидеть все SQL запросы, которые могут помочь вам определить самые медленные из них и использовать EXPLAIN, чтобы увидеть, что там происходит не так.

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