删除Django DB中的重复行

时间:2012-01-22 22:44:22

标签: python django

我有一个模型,因为代码错误,有重复的行。我现在需要删除数据库中的任何重复项。

每一行都应该有一个独特的photo_id。有一种简单的方法可以删除它们吗?或者我需要做这样的事情:

rows = MyModel.objects.all()
for row in rows:
    try:
        MyModel.objects.get(photo_id=row.photo_id)
    except:
        row.delete()

7 个答案:

答案 0 :(得分:24)

最简单的方法是最简单的方法!特别是对于性能甚至不重要的一个脚本(除非它确实如此)。由于它不是核心代码,我只想写出第一个想到的东西,工作

# assuming which duplicate is removed doesn't matter...
for row in MyModel.objects.all():
    if MyModel.objects.filter(photo_id=row.photo_id).count() > 1:
        row.delete()

一如既往,请在执行此操作之前备份。

答案 1 :(得分:12)

这可能会更快,因为它避免了MyModel中每一行的内部过滤器。

由于id是唯一的,如果模型按它们按递增顺序排序,我们可以跟踪我们看到的最后一个id,当我们看到一个具有相同id的模型时,我们走过行,它必须是重复,所以我们可以删除它。

lastSeenId = float('-Inf')
rows = MyModel.objects.all().order_by('photo_id')

for row in rows:
  if row.photo_id == lastSeenId:
    row.delete() # We've seen this id in a previous row
  else: # New id found, save it and check future rows for duplicates.
    lastSeenId = row.photo_id 

答案 2 :(得分:3)

这是一个快速的解决方案:

from django.db import connection

query = "SELECT id FROM table_name GROUP BY unique_column HAVING COUNT(unique_column)>1"
cursor = connection.cursor()
cursor.execute(query)
ids_list = [item[0] for item in cursor.fetchall()]

现在你可以这样做:

Some_Model.objects.filter(id__in=ids_list).delete()

或者如果ids_list太大而无法由您的dbms处理

您可以将其细分为可由其处理的块:

seg_length = 100
ids_lists = [ids_list[x:x+seg_length] for x in range(0,len(ids_list),seg_length)]
for ids_list in ids_lists:
    SomeModel.objects.filter(id__in=ids_list).delete()

答案 3 :(得分:0)

而不是迭代整个表,你可以做到

count = MyModel.objects.filter(photo_id='some_photo_id').count()
while count >=1:
    MyModel.objects.filter(photo_id='some_photo_id')[0].delete()
    count -= 1

答案 4 :(得分:0)

一种通用且经过优化的方法,以防需要删除大量对象-

qs = Model.objects.all()
key_set = set()
delete_ids_list = []
for object in qs:
    object_key = object.unique_key    # photo_id here
    if object_key in key_set:
        delete_ids_list.append(object.id)
    else:
        key_set.add(object_key)
Model.objects.filter(id__in=delete_ids_list).delete()

答案 5 :(得分:0)

从Django 1.11开始,您可以使用

MyModel.objects.annotate(
    count=Subquery(
        MyModel.objects.filter(
            photo_id=OuterRef('photo_id')
        ).values(
            'photo_id'
        ).annotate(
            count=Count('pk')
        ).values('count')
    )
).filter(
    count__gt=1
)

此查询将为您提供没有唯一photo_id的行,然后您可以为每张照片id保留一行并删除其余的

答案 6 :(得分:0)

将其他一些答案与 Window 函数相结合,您可以注释行号。使用分区很重要,否则行号将是连续的。遍历查询集并将行号不是第一次出现的任何对象添加到稍后可以在一个查询中删除的 id 列表中。

from django.db.models import Count, F, IntegerField, OuterRef, Subquery, Window
from django.db.models.functions import RowNumber

from group.models import BuyingGroupTemplate

templates = (
    BuyingGroupTemplate.objects
    .order_by('group', 'product')
    .annotate(
        count=Subquery(
            BuyingGroupTemplate.objects
            .filter(
                group=OuterRef('group'),
                product=OuterRef('product'),
            )
            .values('group', 'product')
            .annotate(count=Count('*'))
            .values('count'),
            output_field=IntegerField(),
        ),
        row_number=Window(
            expression=RowNumber(),
            partition_by=[F('group'), F('product')],
            order_by=F('id').asc()
        ),
    )
    .filter(count__gt=1)
)
ids_to_delete = list()
for template in templates:
    if template.row_number != 1:
        ids_to_delete.append(template.id)

BuyingGroupTemplate.objects.filter(id__in=ids_to_delete).delete()