Django=に以下のように定義されたモデルがあると仮定します。
class Literal:
name = models.CharField(...)
...
名前フィールドは一意ではないため、値が重複する場合があります。次のタスクを実行する必要があります。少なくとも1つの重複する値のname
フィールドを持つモデルからすべての行を選択します。
私はプレーンSQLを使用してそれを行う方法を知っています(最善の解決策ではないかもしれません):
select * from literal where name IN (
select name from literal group by name having count((name)) > 1
);
だから、Django ORM?またはより良いSQLソリューションを使用してこれを選択することは可能ですか?
試してください:
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])
これは編集として拒否されました。したがって、ここではbetter answer
_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をだまして、サブクエリの名前列のみを選択させます。
aggregation を使用してみてください
Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)
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
名前リストのみを生成し、オブジェクトは生成しない場合は、次のクエリを使用できます
repeated_names = Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1).values_list('name', flat='true')