如何使用Django ORM查询关联表中每本书对应的最后50名作者数据
Django ORM 分组取每组前N条性能优化方案
你当前的写法属于典型的N+1查询问题:首先执行1次查询获取所有Book数据,之后每遍历1本图书都会单独发起1次SQL查询获取对应的作者数据,当图书数量较多时会产生大量数据库请求,导致性能极差。
优化方案1:使用Prefetch自定义关联查询(Django 2.0+ 推荐,改动最小)
利用Django内置的Prefetch对象可以自定义关联查询的过滤、排序、切片规则,仅用2次SQL查询就能拿到所有需要的数据,不会产生额外请求。
代码示例:
from django.db.models import Prefetch # 预查询每本书的最新50名作者,自定义关联字段名为latest_50_authors books = Book.objects.prefetch_related( Prefetch( 'author_set', # 这里的排序规则根据你对"最后50名"的定义调整,示例按主键倒序即最新插入的50条 queryset=Author.objects.order_by('-id')[:50], to_attr='latest_50_authors' ) ).all() # 遍历逻辑 for book in books: for author in book.latest_50_authors: # 你的业务逻辑 pass
优化方案2:窗口函数实现(超大数据量场景推荐,性能最优)
如果你的图书总量非常大,想要更极致的查询性能,可以使用窗口函数先给作者按所属图书分组排序打行号,过滤出行号≤50的作者,仅需2次查询就能拿到全量数据,内存开销也更低。
代码示例:
from django.db.models import F, Window from django.db.models.functions import RowNumber # 给每个作者按所属图书分组,按倒序排列后标记行号,只保留每组前50条 authors = Author.objects.annotate( row_num=Window( expression=RowNumber(), partition_by=F('book_id'), # 排序规则可根据业务调整,示例按主键倒序 order_by=F('id').desc() ) ).filter(row_num__lte=50).select_related('book') # 组装成图书对应作者的映射结构 book_author_map = {} for author in authors: book = author.book if book.id not in book_author_map: book_author_map[book.id] = { "book": book, "authors": [] } book_author_map[book.id]["authors"].append(author) # 遍历逻辑 for item in book_author_map.values(): book = item["book"] for author in item["authors"]: # 你的业务逻辑 pass
内容的提问来源于stack exchange,提问作者Dima
相关产品推荐
相关产品推荐

