如何在Django中正确使用prefetch_related?为何聚合查询性能差异悬殊?
问题原因
你这里用prefetch_related完全无效,因为聚合操作是在数据库层面执行的,而prefetch_related的作用是在Python层面批量预取关联模型数据、避免N+1查询——它根本不会影响聚合查询的SQL生成,反而会让Django额外执行一次PZ表的查询,拖慢整体速度。
第二条语句PZ.objects.prefetch_related('pzitem_set').aggregate(Sum('pzitem__quantity'))的实际执行逻辑是:
- 先查询所有PZ记录
- 再批量查询关联的PZItem记录(这一步是
prefetch_related做的,但完全多余) - 最后仍会生成一条JOIN PZ和PZItem的SQL语句计算总和,等于做了无用的额外查询,自然比直接查PZItem慢很多。
正确用法
场景1:计算所有PZItem的quantity总和
直接用第一条语句即可,这是效率最高的方式:
test = PZItem.objects.all().aggregate(Sum('quantity'))
场景2:按每个PZ分组,计算单PZ对应的PZItem数量总和
用annotate实现,同样不需要prefetch_related:
# 按PZ分组,得到每个PZ及其对应的PZItem quantity总和 pz_with_sum = PZ.objects.annotate(total_quantity=Sum('pzitem__quantity'))
场景3:确实需要预取关联数据,在Python层面手动求和
只有当你需要同时获取PZ的其他字段,且要在Python中处理关联数据时,prefetch_related才有用,但这种方式仅适合数据量小的场景,性能不如数据库层面聚合:
pzs = PZ.objects.prefetch_related('pzitem_set').all() # Python层面求和 total = sum(item.quantity for pz in pzs for item in pz.pzitem_set.all())
总结:prefetch_related是给需要在Python中访问关联模型数据的场景设计的,数据库层面的聚合/分组查询不需要它,直接用ORM的aggregate或annotate即可。
内容的提问来源于stack exchange,提问作者skelaw
相关产品推荐
相关产品推荐

