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

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只有namecount.但是,您可以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)

  • 可能你的意思是``Literal.objects.values('name').annotate(name_count = Count('name')).filter(name_count__gt = 1)``? (4认同)
  • 感谢更新的答案,我想我会坚持使用这个解决方案,你甚至可以通过使用``values_list('name',flat = True)来实现它而不需要列表理解. (2认同)
  • Django 之前在这方面有一个错误(可能在最近的版本中已修复),如果您没有为 `Count` 注释指定字段名以保存为,则默认为 `[field]__count`。但是,双下划线语法也是 Django 解释您想要进行连接的方式。因此,本质上当您尝试过滤时,Django 认为您正在尝试与显然不存在的 `count` 进行连接。解决方法是为您的注释结果指定一个名称,即`annotate(mycount=Count('id'))`,然后在`mycount` 上进行过滤。 (2认同)

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列.

  • @stefanfoulis它清除任何现有的订单.如果你有一个模型集排序,这将成为SQL`GROUP BY`子句的一部分,这会破坏事情.在玩Subquery时发现它(通过`.values()`进行非常相似的分组) (3认同)
  • `.order_by()` 是做什么用的? (2认同)

Jam*_*esO 9

尝试使用聚合

Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)
Run Code Online (Sandbox Code Playgroud)


Eug*_*mov 6

如果您使用 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)