如何在Django中获取每个tagid对应的最新一条数据?
问题描述
我有如下Django抽象模型:
class TagDataBase(BaseModel): tagid = models.IntegerField(primary_key=True) intvalue = models.BigIntegerField(blank=True, null=True) floatvalue = models.FloatField(blank=True, null=True) stringvalue = models.CharField(max_length=255, blank=True, null=True) datevalue = models.DateTimeField(blank=True, null=True) dataintegrity = models.IntegerField(blank=True, null=True) t_stamp = TimestampField() class Meta: managed = False abstract = True db_table = 'sqlt_data_1_2023_02' unique_together = (('tagid', 't_stamp'),)
该模型为抽象类,数据库表会动态变化,我通过get_model()获取模型类。当前执行的查询如下:
tag_ids = [list of tagid] q = Q(tagid__in=tag_ids, dataintegrity=192) items = get_model().objects.filter(q).order_by('-t_stamp')
这个查询能正常运行,但会返回数十万条冗余数据。我仅需要每个tagid对应的最新数据(按t_stamp降序排列的第一条)。
注意事项:
- 模型不由Django管理,无法修改字段
- 使用的数据库为MariaDB 10.9
解决方案
针对MariaDB 10.9的特性,推荐两种高效实现方式:
方法1:使用窗口函数RowNumber()
利用Django的Window和RowNumber函数,按tagid分组并按t_stamp降序标记行号,再筛选每组的第一条数据:
from django.db.models import Window, F from django.db.models.functions import RowNumber tag_ids = [list of tagid] model_cls = get_model() # 为每条数据标注其在对应tagid组内的行号(按t_stamp降序) annotated_items = model_cls.objects.filter( tagid__in=tag_ids, dataintegrity=192 ).annotate( row_num=Window( expression=RowNumber(), partition_by=F('tagid'), order_by=F('t_stamp').desc() ) ) # 筛选出每组的第一条记录 latest_items = annotated_items.filter(row_num=1)
该方法生成的SQL利用窗口函数一次性完成分组排序与筛选,性能最优,避免冗余数据加载到内存。
方法2:子查询获取每个tagid的最新t_stamp
先查询每个tagid对应的最新t_stamp,再关联获取对应数据:
from django.db.models import Subquery, OuterRef tag_ids = [list of tagid] model_cls = get_model() # 子查询:获取单个tagid的最新t_stamp latest_tstamp_subquery = model_cls.objects.filter( tagid=OuterRef('tagid'), dataintegrity=192 ).order_by('-t_stamp').values('t_stamp')[:1] # 主查询:匹配tagid和对应最新t_stamp latest_items = model_cls.objects.filter( tagid__in=tag_ids, dataintegrity=192, t_stamp=Subquery(latest_tstamp_subquery) )
这种方法兼容性强,适合不熟悉窗口函数的场景,数据量极大时性能略逊于方法1。
关键注意点
- 依赖模型中已定义的
unique_together = (('tagid', 't_stamp'),)约束,避免单个tagid出现多条相同t_stamp的最新数据 - 两种方法均只会返回每个tagid的唯一最新记录,不会加载冗余数据
内容的提问来源于stack exchange,提问作者cclloyd
相关产品推荐
相关产品推荐

