Django同时使用多个annotate Count统计结果异常如何解决
问题原因
这是Django ORM执行多关联聚合时的笛卡尔积重复计数问题。
当你在同一个annotate中同时统计两个独立的多对多关联(本例中是Post的直接likes多对多、以及Post关联Comment下的likes多对多)时,ORM会将两张关联表都和Post表做LEFT JOIN,最终生成的中间结果行数是两个关联表匹配行数的乘积:
比如delta这个Post,本身likes有3条关联记录,comment__likes有6条关联记录,JOIN后的中间表就会产生3*6=18条重复的Post行,Count统计时会把所有行都计入结果,所以两个统计值都变成了18,和你看到的错误结果完全吻合。如果某个关联没有匹配记录(比如gamma的comment__likes是0),乘积就等于原有匹配行数,所以结果恰好符合预期。
解决方法
方案1:给Count添加distinct=True(最简单,适用于绝大多数场景)
直接在统计时开启去重计数,避免重复行被多次统计,代码如下:
Post.objects.annotate( likecount=Count('likes', distinct=True), commentlikecount=Count('comment__likes', distinct=True) ).values('content','likecount','commentlikecount')
执行后即可得到你预期的合并统计结果。
方案2:使用子查询(适用于大数据量、关联关系复杂的场景,性能更优)
如果数据量很大,多表JOIN性能差,可以用子查询分别统计两个值,避免大表关联产生的性能损耗:
from django.db.models import Count, OuterRef, Subquery # 子查询统计每个Post的点赞数 like_subq = Post.objects.filter(pk=OuterRef('pk')).annotate( likecount=Count('likes') ).values('likecount') # 子查询统计每个Post的评论总点赞数 comment_like_subq = Post.objects.filter(pk=OuterRef('pk')).annotate( commentlikecount=Count('comment__likes') ).values('commentlikecount') # 主查询关联子查询结果 Post.objects.annotate( likecount=Subquery(like_subq), commentlikecount=Subquery(comment_like_subq) ).values('content','likecount','commentlikecount')
内容的提问来源于stack exchange,提问作者RoyalBosS
相关产品推荐
相关产品推荐

