我有两种型号,Author
和Book
:
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_price
,higher_price
和预取)非常相似的子查询被执行,而且速度没有那么快。如何优化它? 最佳答案
现在当我得到一组作者和他们的书,我想注释
每本书有两个值,表明它是最便宜的还是最便宜的
从同一个作者的筛选出来的书很贵。
它可以工作,但会导致三种情况(更低的价格、更高的价格和
预取)正在执行非常相似的子查询,速度没有那么快。
如何优化它?
你不能逃避这三个问题
以某种形式。
有3个类似的查询并不意味着它们比单个查询慢3倍,这需要更多的调查才能找到瓶颈所在,可能是缺少索引或smt else。
指出一本书是最便宜的还是最贵的意味着你需要将书价与一些最低/最高价格进行比较。一旦有了这个最低/最高价格,比较就很容易了。既然你想把这本书与QuerySet中作者的其他书进行比较,那么在Authors QuerySet中就更有意义了。例如:
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