dra*_*oon 86 sql django django-orm
假设我们在django中有一个模型定义如下:
class Literal:
name = models.CharField(...)
...
Run Code Online (Sandbox Code Playgroud)
名称字段不是唯一的,因此可以具有重复值.我需要完成以下任务:从模型中选择至少具有该name字段的重复值的所有行.
我知道如何使用纯SQL(可能不是最佳解决方案):
select * from literal where name IN (
select name from literal group by name having count((name)) > 1
);
Run Code Online (Sandbox Code Playgroud)
那么,是否可以使用django ORM选择它?或更好的SQL解决方案?
Chr*_*att 171
尝试:
from django.db.models import Count
Literal.objects.values('name')
.annotate(Count('id'))
.order_by()
.filter(id__count__gt=1)
Run Code Online (Sandbox Code Playgroud)
这与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])
Run Code Online (Sandbox Code Playgroud)
Pip*_*iam 37
这被编辑拒绝了.所以这里是一个更好的答案
dups = (
Literal.objects.values('name')
.annotate(count=Count('id'))
.values('name')
.order_by()
.filter(count__gt=1)
)
Run Code Online (Sandbox Code Playgroud)
这将返回包含所有重复名称的ValuesQuerySet.但是,您可以使用它来构造常规QuerySet,方法是将其反馈到另一个查询中.django orm非常聪明,可以将这些组合成一个查询:
Literal.objects.filter(name__in=dups)
Run Code Online (Sandbox Code Playgroud)
在注释调用之后额外调用.values('name')看起来有点奇怪.没有它,子查询失败.额外的值会使orm进入,只选择子查询的name列.
尝试使用聚合
Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)
Run Code Online (Sandbox Code Playgroud)
如果您使用 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))
Run Code Online (Sandbox Code Playgroud)
它导致这个相当简单的 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
Run Code Online (Sandbox Code Playgroud)