使用指示字段的最小值或最大值的值来注释每个结果

时间:2018-02-05 11:32:30

标签: python django database query-optimization

我有两个模型,AuthorBook

class Author(models.Model):
    name = models.CharField(max_length=256)

class Book(models.Model):
    name = models.CharField(max_length=256)
    price = models.IntegerField()
    publication_date = models.DateField()
    author = models.ForeignKey(Author, related_name="books")

现在当我得到一组作者和他们的书时,我想用两个值来注释每本书,这两个值表明它是同一作者的过滤书中最便宜或最昂贵的。

我使用Exists和注释

得到了正确的结果
filtered_books = Book.objects.filter(publication_date__year=2010)

lower_price = filtered_books.only('id').filter(price__lt=OuterRef('price'), author=OuterRef('author'))
higher_price = filtered_books.only('id').filter(price__gt=OuterRef('price'), author=OuterRef('author'))

filtered_books = filtered_books.annotate(
    lowest_price=~Exists(lower_price),
    highest_price=~Exists(higher_price),
)

authors = Author.objects.annotate.prefetch_related(Prefetch('books', queryset=filtered_books))

它可以工作,但会导致三个(lower_pricehigher_price和预取)非常相似的子查询被执行并且不那么快。我该如何优化呢?

1 个答案:

答案 0 :(得分:1)

  

现在当我得到一组作者和他们的书时,我想要注释   每本书都有两个值,表明它是最便宜的还是最多的   来自同一作者的过滤书中的价格昂贵。

     

它有效,但结果有三个(lower_price,higher_price和   prefetch)执行非常相似的子查询,并不是那么快。   我该如何优化呢?

  • 您无法从这3个查询中逃脱,它们将被展示出来 以某种形式。

  • 有3个类似的查询并不意味着它们比单个查询慢3倍,这需要更多的调查才能找到瓶颈的位置,它可能是缺失的索引或smt else。

  • 指示您是否需要将图书价格与某些最低/最高价格进行比较,这是一本最便宜或最便宜的图书。一旦达到最低/最高价格,比较就很容易了。由于您希望将这本书与作者在查询集中的其他书籍进行比较,因此在作者查询集中对此更有意义。一个例子:

    filtered_books = Book.objects.filter(publication_date__year=2010)
    
    min_price_subquery = (filtered_books
        .filter(author=OuterRef('pk'))
        .values('author')
        .annotate(min_price=Min('price'))
        .only('min_price')
    )
    
    max_price_subquery = (filtered_books
        .filter(author=OuterRef('pk'))
        .values('author')
        .annotate(max_price=Max('price'))
        .only('max_price')
    )
    
    authors = Author.objects.annotate(
        min_book_price=Subquery(min_price_subquery, output_field=models.IntegerField()),
        max_book_price=Subquery(max_price_subquery, output_field=models.IntegerField())
    ).prefetch_related(Prefetch('books', queryset=filtered_books))
    
    for author in authors:
        for book in author.books:
            if book.price == author.min_book_price:
                #cheapest price
            if book.price == author.max_book_price:
                #most expensive