Django 仅选择具有重复字段值的行

问题描述 投票:0回答:6

假设我们在 django 中有一个模型定义如下:

class Literal:
    name = models.CharField(...)
    ...

名称字段不唯一,因此可能有重复的值。我需要完成以下任务: 从模型中选择具有 name 字段

至少一个重复值
的所有行。

我知道如何使用普通 SQL 来做到这一点(可能不是最好的解决方案):

select * from literal where name IN (
    select name from literal group by name having count((name)) > 1
);

那么,是否可以使用 django ORM 选择它?或者更好的 SQL 解决方案?

sql django django-orm
6个回答
288
投票

尝试:

from django.db.models import Count
Literal.objects.values('name')
               .annotate(Count('id')) 
               .order_by()
               .filter(id__count__gt=1)

这是 Django 所能达到的最接近的效果。问题是这将返回一个仅包含

ValuesQuerySet
name
count
。但是,您可以使用它来构造常规
QuerySet
,将其反馈到另一个查询中:

dupes = Literal.objects.values('name')
                       .annotate(Count('id'))
                       .order_by()
                       .filter(id__count__gt=1)
Literal.objects.filter(name__in=[item['name'] for item in dupes])

68
投票

此内容因编辑而被拒绝。所以这是一个更好的答案

dups = (
    Literal.objects.values('name')
    .annotate(count=Count('id'))
    .values('name')
    .order_by()
    .filter(count__gt=1)
)

这将返回包含所有重复名称的

ValuesQuerySet
。但是,您可以使用它通过将其反馈到另一个查询中来构造常规
QuerySet
。 django ORM 足够智能,可以将这些组合成一个查询:

Literal.objects.filter(name__in=dups)

注释调用后对

.values('name')
的额外调用看起来有点奇怪。如果没有这个,子查询就会失败。额外的值会欺骗 ORM 仅选择子查询的名称列。


12
投票

尝试使用聚合

Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)

8
投票

如果您使用 PostgreSQL,您可以执行以下操作:

from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models import Func, Value, DecimalField

duplicate_ids = (Literal.objects.values('name')
                 .annotate(ids=ArrayAgg('id'))
                 .annotate(c=Func('ids', Value(1), function='array_length', output_field=DecimalField()))
                 .filter(c__gt=1)
                 .annotate(ids=Func('ids', function='unnest'))
                 .values_list('ids', flat=True))

它会产生这个相当简单的 SQL 查询:

SELECT unnest(ARRAY_AGG("app_literal"."id")) AS "ids"
FROM "app_literal"
GROUP BY "app_literal"."name"
HAVING array_length(ARRAY_AGG("app_literal"."id"), 1) > 1

2
投票

好吧,由于某种原因,上述方法都不起作用,它总是返回

<MultilingualQuerySet []>
。我使用以下更容易理解但不是那么优雅的解决方案:

dupes = []
uniques = []

dupes_query = MyModel.objects.values_list('field', flat=True)

for dupe in set(dupes_query):
    if not dupe in uniques:
        uniques.append(dupe)
    else:
        dupes.append(dupe)

print(set(dupes))

0
投票

如果您只想结果名称列表而不是对象,您可以使用以下查询

repeated_names = Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1).values_list('name', flat='true')
© www.soinside.com 2019 - 2024. All rights reserved.