如何用Django ORM按标题获取对应最高频分类的行数据
如何用Django ORM为每个title保留出现次数最多的category
需求
开发收支分类处理任务,数据源来自交易应用。需要为每个title获取对应出现次数最多的category:同一title对应多个分类时,仅保留计数最高的分类行,且全程用Django ORM实现(多数据库场景下比遍历大数据列表更高效)。
模型定义
class Movimentation(models.Model): title = models.CharField(max_length=50) value = models.FloatField() category = models.CharField(max_length=50)
当前实现及问题
当前查询
Movimentation.objects \ .values('title', 'category') \ .annotate(count=Count('*'))
当前结果
[ {'title': 'Pizza', 'category': 'food', 'count': 6}, {'title': 'Pizza', 'category': 'others_expense', 'count': 1}, {'title': 'Pizza', 'category': 'refund', 'count': 1}, {'title': 'Hamburguer', 'category': 'food', 'count': 1}, {'title': 'Clothing', 'category': 'personal', 'count': 18}, {'title': 'Clothing', 'category': 'home', 'count': 15}, {'title': 'Clothing', 'category': 'others_expense', 'count': 1} ]
期望结果
[ {'title': 'Pizza', 'category': 'food', 'count': 6}, {'title': 'Hamburguer', 'category': 'food', 'count': 1}, {'title': 'Clothing', 'category': 'personal', 'count': 18} ]
解决方案
利用Django的**窗口函数(Window)和排名函数(Rank)**实现,无需遍历数据列表,直接通过ORM生成高效的SQL查询:
代码实现
首先导入所需模块:
from django.db.models import Count, Window, F from django.db.models.functions import Rank
然后执行查询:
# 第一步:统计每个title-category组合的出现次数 category_counts = Movimentation.objects.values('title', 'category').annotate(count=Count('*')).order_by() # 第二步:用窗口函数为每个title下的category按count降序排名,过滤出排名第一的结果 result = category_counts.annotate( rank=Window( expression=Rank(), partition_by=F('title'), # 按title分组 order_by=F('count').desc() # 按count降序排序,次数多的排前面 ) ).filter(rank=1).values('title', 'category', 'count')
说明
order_by():移除默认排序规则,避免干扰窗口函数的分区排序逻辑;Window+Rank():为每个title分组内的category按count降序生成排名;filter(rank=1):仅保留每个title下排名第一(即计数最高)的分类记录。
如果存在多个分类计数相同且均为最高的情况,Rank()会返回相同排名,此时会保留多条记录。若需仅保留其中一条,可改用DenseRank()或RowNumber()(根据业务需求选择)。
内容的提问来源于stack exchange,提问作者Kaspary
相关产品推荐
相关产品推荐

