Django只select具有重复字段值的行
假设我们在django中有一个模型定义如下:
class Literal: name = models.CharField(...) ...
名称字段不是唯一的,因此可能有重复的值。 我需要完成以下任务:从模型中select至less有一个 name
字段重复值的所有行。
我知道如何使用纯SQL(可能不是最好的解决scheme):
select * from literal where name IN ( select name from literal group by name having count((name)) > 1 );
那么,是否可以使用django ORM来select它? 或更好的SQL解决scheme?
尝试:
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])
这被作为编辑拒绝。 所以这里是一个更好的答案
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进入只为子查询select名称列。
尝试使用聚合
Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)
如果您只想得到名称列表而不是对象,则可以使用以下查询
repeated_names = Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1).values_list('name', flat='true')
如果你使用PostgreSQL,你可以这样做:
from django.contrib.postgres.aggregates import ArrayAgg from django.db.models import Func, Value duplicate_ids = (Literal.objects.values('name') .annotate(ids=ArrayAgg('id')) .annotate(c=Func('ids', Value(1), function='array_length')) .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