Django+PostgreSQL如何查询用户重复激活日期记录并停用多余旧记录
解决方案
方案1:Django ORM 实现(无原生SQL,适配性好)
利用Django的子查询功能,仅需两次数据库查询即可完成所有更新,完全避免Python层遍历:
from django.db.models import OuterRef, Subquery # 1. 构造子查询:匹配每个user_id+sp_date分组下最新的激活记录ID latest_active = DateClass.objects.filter( is_active=True, user_id=OuterRef("user_id"), sp_date=OuterRef("sp_date") # 按主键倒序取第一条,有created_at字段建议替换为order_by("-created_at") ).order_by("-id").values("id")[:1] # 可选:执行前先统计需要更新的记录数,验证逻辑正确性 # update_count = DateClass.objects.filter(is_active=True).exclude(id=Subquery(latest_active)).count() # print(f"待更新重复记录数:{update_count}") # 2. 批量更新所有重复的旧激活记录 DateClass.objects.filter(is_active=True).exclude(id=Subquery(latest_active)).update(is_active=False)
方案2:PostgreSQL 原生SQL实现(性能最优,适合大数据量场景)
直接用PostgreSQL的窗口函数,单次SQL即可完成操作,性能比ORM方案更高:
WITH ranked_active_records AS ( SELECT id, -- 按user_id、sp_date分组,按插入顺序倒排(id自增可用id倒序,有created_at优先用created_at) ROW_NUMBER() OVER (PARTITION BY user_id, sp_date ORDER BY id DESC) AS row_num FROM 你实际的表名 -- 替换为DateClass对应的数据库表名,默认格式为`你的app名_dateclass` WHERE is_active = TRUE ) UPDATE 你实际的表名 SET is_active = FALSE WHERE id IN (SELECT id FROM ranked_active_records WHERE row_num > 1);
注意事项
- 如果你模型中有
created_at之类的创建时间字段,建议排序时优先用创建时间字段,比自增ID判断新旧更准确 - 首次执行前建议先跑统计逻辑确认待更新的记录数符合预期,避免误操作
- 两种方案的时间复杂度均为O(n),仅和表内总数据量相关,远优于原遍历方案的O(n²)级复杂度
内容的提问来源于stack exchange,提问作者Akash
相关产品推荐
相关产品推荐

