如何在Django中按条件高效批量删除重复数据?
处理2600万行数据的重复删除方案
一、Django ORM实现(可行但性能受限)
Django ORM确实能实现需求,但面对2600万行的超大表,直接全量操作会有明显性能瓶颈,建议分批次执行:
实现步骤
- 先查询每组(
share_class__id+date)中需保留的记录ID:
from django.db.models import Max, Subquery, OuterRef # 子查询获取每个分组最新updated对应的记录ID latest_group_ids = MyModel.objects.filter( share_class__id=OuterRef('share_class__id'), date=OuterRef('date') ).values('share_class__id', 'date').annotate( max_updated=Max('updated') ).values('id') # 提取所有需保留的ID集合 keep_ids = MyModel.objects.filter(id__in=Subquery(latest_group_ids)).values_list('id', flat=True)
- 分批次删除非保留记录:
batch_size = 10000 total_deleted = 0 while True: # 每次获取一批待删除的ID delete_ids = list(MyModel.objects.exclude(id__in=keep_ids)[:batch_size].values_list('id', flat=True)) if not delete_ids: break # 执行删除 deleted, _ = MyModel.objects.filter(id__in=delete_ids).delete() total_deleted += deleted print(f"已删除 {total_deleted} 条重复数据")
注意:此方式会生成超大的keep_ids集合,占用大量应用层内存,且循环批次多,整体执行效率偏低,仅适合小批量数据场景。
二、原生SQL实现(高效推荐)
针对2600万行的超大表,原生SQL配合窗口函数是最优解——直接在数据库层面处理,避免大量数据传输到应用层,性能提升显著。
PostgreSQL 写法
WITH ranked_records AS ( SELECT id, ROW_NUMBER() OVER ( PARTITION BY share_class__id, date ORDER BY updated DESC ) AS rn FROM myapp_mymodel -- 替换为你的实际表名(格式:app名_模型名小写) ) DELETE FROM myapp_mymodel WHERE id IN (SELECT id FROM ranked_records WHERE rn > 1);
MySQL 8.0+ 写法
WITH ranked_records AS ( SELECT id, ROW_NUMBER() OVER ( PARTITION BY share_class__id, date ORDER BY updated DESC ) AS rn FROM myapp_mymodel ) DELETE FROM myapp_mymodel USING myapp_mymodel JOIN ranked_records ON myapp_mymodel.id = ranked_records.id WHERE ranked_records.rn > 1;
关键优化建议
- 备份数据:执行删除前务必全量备份,避免不可逆损失。
- 添加索引:提前创建复合索引加速分组排序:
CREATE INDEX idx_share_date_updated ON myapp_mymodel(share_class__id, date, updated DESC); - 分段处理:若单条SQL执行超时,可按
date分段,比如按月份拆分执行删除逻辑。
总结
- 小批量数据用Django ORM即可满足需求;但2600万行的超大表场景,原生SQL是唯一高效的选择,能规避应用层内存压力和数据传输开销。
- 无论采用哪种方案,优先添加合适的索引是提升性能的核心。
内容的提问来源于stack exchange,提问作者Gilles Criton
相关产品推荐
相关产品推荐

