如何使用Django ORM去除查询结果中value字段值相同的重复记录
实现方案
完全可以通过Django ORM实现,也可以用兼容性更强的内存处理方案,两种方式都能满足你过滤连续重复value、仅保留值变化节点的需求,最终返回的数据量可以压缩到你预期的8000条左右。
方案1:数据库层实现(推荐,支持PostgreSQL/MySQL 8.0+/SQLite 3.25+)
利用窗口函数Lag获取同个指标(同机器、同name)的上一条记录的value,仅保留第一条或者value和上一条不同的记录即可。
代码修改
首先导入依赖:
from django.db.models import Window, F, Q from django.db.models.functions import Lag
然后在你原有的查询过滤逻辑之后、分页之前,增加以下代码:
# 先按时间正序排序,保证相邻记录的时间顺序正确 queryset = queryset.order_by('date_time') # 新增字段,存储同个机器同个指标的上一条记录的value queryset = queryset.annotate( prev_value=Window( expression=Lag('value'), # 按机器、指标name分组,不同分组的记录互不影响 partition_by=[F('machine_id'), F('name')], order_by=F('date_time').asc() ) ) # 仅保留第一条记录,或者value和上一条不同的记录 queryset = queryset.filter(Q(prev_value__isnull=True) | ~Q(prev_value=F('value')))
之后再走原有分页、序列化逻辑即可,返回的结果就是已经去重连续相同value的数据集。
方案2:应用层内存处理(全数据库兼容)
如果你的数据库版本不支持窗口函数,直接对查询结果做内存过滤即可,2.5万条数据的处理耗时仅为毫秒级,完全不会影响性能。
代码修改
在你原有查询过滤逻辑之后,替换原有分页、序列化逻辑为以下代码:
# 先按指标名称、时间排序 data_list = list(queryset.order_by('name', 'date_time')) filtered_data = [] current_name = None last_value = None for item in data_list: # 遇到新的指标直接保留 if item.name != current_name: filtered_data.append(item) current_name = item.name last_value = item.value # 同指标下value变化才保留 else: if item.value != last_value: filtered_data.append(item) last_value = item.value # 对过滤后的数据做分页和序列化 pagination = LargeResultsSetPagination() paginated_qs = pagination.paginate_queryset(filtered_data, request) serializer = TechnicalDataSerializer(paginated_qs, many=True) return Response(serializer.data)
注意事项
- 两种方案都是过滤连续重复的value,不会删除非连续的相同值记录,完全符合折线图的展示要求,不会丢失值变化的节点
- 如果你的业务逻辑允许,优先选方案1,数据库层处理的性能更好,也支持原生分页
- 如果指标和机器维度很多,内存处理的压力也不会明显上升,最多也就几万条数据,完全在Python的处理能力范围内
内容的提问来源于stack exchange,提问作者darkvodka
相关产品推荐
相关产品推荐

