Улучшение производительности Django с 350000+ regs и сложным запросом - PullRequest
1 голос
/ 30 августа 2011

У меня есть такая модель:

class Stock(models.Model):
    product = models.ForeignKey(Product)
    place = models.ForeignKey(Place)
    date = models.DateField()
    quantity = models.IntegerField()

Мне нужно получить самую последнюю версию (по date) quantity за каждые product за каждые place, с почти 500 продуктами,100 мест и 350000 складских записей в базе данных.

Мой текущий код такой, он работал на тестировании, но с реальными данными так долго, что бесполезен

    stocks = Stock.objects.filter(product__in=self.products,
                                  place__in=self.places, date__lt=date_at)
    stock_values = {}
    for prod in self.products:
        for place in self.places:
            key = u'%s%s' % (prod.id, place.id)
            stock = stocks.filter(product=prod, place=place, date=date_at)
            if len(stock) > 0:
                stock_values[key] = stock[0].quantity
            else:
                try:
                    stock = stocks.filter(product=prod, place=place).order_by('-date')[0]
                except IndexError:
                    stock_values[key] = 0
                else:
                    stock_values[key] = stock.quantity
    return stock_values

Как быВы делаете это быстрее?

Редактировать: Переписал код следующим образом:

    stock_values = {}
    for product in self.products:
        for place in self.places:
            try:
                stock_value = Stock.objects.filter(product=product, place=place, date__lte=date_at)\
                                           .order_by('-date').values('cant')[0]['cant']
            except IndexError:
                stock_value = 0
            stock_values[u'%s%s' % (product.id, place.id)] = stock_value
    return stock_values

Он работает лучше (от 256 секунд до 64), но все же нужно улучшить его,Может быть, какой-то пользовательский SQL, я не знаю ...

Ответы [ 2 ]

2 голосов
/ 30 августа 2011

Артур прав, len(stock) не самый эффективный способ сделать это.Вы могли бы пойти дальше по маршруту «проще просить прощения, чем разрешения» с чем-то вроде этого во внутреннем цикле:

key = u'%s%s' % (prod.id, place.id)
try:
    stock = stocks.filter(product=prod, place=place, date=date_at)[0]
    quantity = stock.quantity
except IndexError:
    try:
        stock = stocks.filter(product=prod, place=place).order_by('-date')[0]
        quantity = stock.quantity
    except IndexError:
        quantity = 0
stock_values[key] = quantity

Я не уверен, насколько это улучшится по сравнению с простым изменениемпроверка длины, хотя я думаю, что это должно по крайней мере ограничить его двумя запросами с LIMIT 1 на них (см. Ограничение QuerySets ).

Имейте в виду, это все еще выполняет много базы данныхудары, так как вы можете пройти через этот цикл почти 50000 раз.Оптимизируйте, как вы зацикливаетесь, и вы все еще в лучшем положении.

0 голосов
/ 30 августа 2011

возможно, дело в том, что метод len ()!

следовать документам из :

Примечание. Не используйте len () в QuerySets, если все, что вам нужно, это определить количество записей в наборе. Гораздо эффективнее справиться с считать на уровне базы данных, используя SQL SELECT COUNT (*) и Django предоставляет метод count () именно по этой причине. Посмотреть количество () ниже.

Так что попробуйте изменить len на count (), и посмотрите, будет ли он быстрее!

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