Count 注释为所有字段添加不需要的 group by 语句
Count annotation adds unwanted group by statement for all fields
我想生成以下查询:
select id, (select count(*) from B where B.x = A.x) as c from A
Subquery 表达式应该很简单。除了我在我的计数查询中添加了一个 group by
语句,我无法摆脱它:
from django.contrib.contenttypes.models import ContentType
str(ContentType.objects.annotate(c=F('id')).values('c').query)
# completely fine query with annotated field
'SELECT "django_content_type"."id" AS "c" FROM "django_content_type"'
str(ContentType.objects.annotate(c=Count('*')).values('c').query)
# gets group by for every single field out of nowhere
'SELECT COUNT(*) AS "c" FROM "django_content_type" GROUP BY "django_content_type"."id", "django_content_type"."app_label", "django_content_type"."model"'
这使得结果为 [{'c': 1}, {'c': 1}, {'c': 1}, {'c': 1},...]
而不是 [{c:20}]
。但是子查询必须只有一行结果才能使用。
由于查询应该在子查询中使用,我不能使用 .count()
或 .aggregate()
,因为它们会立即求值并抱怨 OuterRef
表达式的使用。
子查询示例:
str(ContentType.objects.annotate(fields=Subquery(
Field.objects.filter(model_id=OuterRef('pk')).annotate(c=Count('*')).values('c')
)).query)
生成
SELECT "django_content_type"."id",
"django_content_type"."app_label",
"django_content_type"."model",
(SELECT COUNT(*) AS "c"
FROM "meta_field" U0
WHERE U0."model_id" = ("django_content_type"."id")
GROUP BY U0."id", U0."model_id", U0."module", U0."name", U0."label", U0."widget", U0."visible", U0."readonly",
U0."desc", U0."type", U0."type_model_id", U0."type_meta_id", U0."is_type_meta", U0."multi",
U0."translatable", U0."conditions") AS "fields"
FROM "django_content_type"
预期查询:
SELECT "django_content_type"."id",
"django_content_type"."app_label",
"django_content_type"."model",
(SELECT COUNT(*) AS "c"
FROM "meta_field" U0
WHERE U0."model_id" = ("django_content_type"."id")) AS "fields"
FROM "django_content_type"
更新:(添加来自评论中请求的真实应用程序的模型):
class Translation(models.Model):
field = models.ForeignKey(MetaField, models.CASCADE)
ref_id = models.IntegerField()
# ... other fields
class Choice(models.Model):
meta = models.ForeignKey(MetaField, on_delete=models.PROTECT)
# ... other fields
我需要一个查询来获取每个选项可用的翻译数量,其中 Translation.field_id
指的是 Choice.meta_id
,Translation.ref_id
指的是 Choice.id
。
没有外键的原因是并非所有元字段都是选择字段(例如,文本字段也可能有翻译)。我可以为每个 translatable 实体创建一个单独的 table,但是这个设置应该很容易与其中没有 group by
语句的计数子查询一起使用。
UPDATE 这是一个使用子查询的查询,应该接近您想要的结果:
str(ContentType.objects.annotate(fields=Subquery(
Field.objects.filter(model_id=OuterRef('pk')).values('model').annotate(c=Count('pk')).values('c')
)).query)
我唯一做的就是添加 values('model')
group_by 子句,这使得 Count('pk')
真正起作用,因为它将所有行聚合为一个。
当没有相关行时,它将 return null
而不是 0
,您可以使用 Coalesce
函数或 Case ... When ... then
。
使用 Django ORM 无法实现您想要的确切查询,尽管您可以使用
获得相同的结果
Choice.objects.annotate(c=Count(
'meta__translation',
distinct=True,
filter=Q(meta__translation__ref_id=F('id'))
))
或者看看django-sql-utils package, as also mentioned in this post。
我想生成以下查询:
select id, (select count(*) from B where B.x = A.x) as c from A
Subquery 表达式应该很简单。除了我在我的计数查询中添加了一个 group by
语句,我无法摆脱它:
from django.contrib.contenttypes.models import ContentType
str(ContentType.objects.annotate(c=F('id')).values('c').query)
# completely fine query with annotated field
'SELECT "django_content_type"."id" AS "c" FROM "django_content_type"'
str(ContentType.objects.annotate(c=Count('*')).values('c').query)
# gets group by for every single field out of nowhere
'SELECT COUNT(*) AS "c" FROM "django_content_type" GROUP BY "django_content_type"."id", "django_content_type"."app_label", "django_content_type"."model"'
这使得结果为 [{'c': 1}, {'c': 1}, {'c': 1}, {'c': 1},...]
而不是 [{c:20}]
。但是子查询必须只有一行结果才能使用。
由于查询应该在子查询中使用,我不能使用 .count()
或 .aggregate()
,因为它们会立即求值并抱怨 OuterRef
表达式的使用。
子查询示例:
str(ContentType.objects.annotate(fields=Subquery(
Field.objects.filter(model_id=OuterRef('pk')).annotate(c=Count('*')).values('c')
)).query)
生成
SELECT "django_content_type"."id",
"django_content_type"."app_label",
"django_content_type"."model",
(SELECT COUNT(*) AS "c"
FROM "meta_field" U0
WHERE U0."model_id" = ("django_content_type"."id")
GROUP BY U0."id", U0."model_id", U0."module", U0."name", U0."label", U0."widget", U0."visible", U0."readonly",
U0."desc", U0."type", U0."type_model_id", U0."type_meta_id", U0."is_type_meta", U0."multi",
U0."translatable", U0."conditions") AS "fields"
FROM "django_content_type"
预期查询:
SELECT "django_content_type"."id",
"django_content_type"."app_label",
"django_content_type"."model",
(SELECT COUNT(*) AS "c"
FROM "meta_field" U0
WHERE U0."model_id" = ("django_content_type"."id")) AS "fields"
FROM "django_content_type"
更新:(添加来自评论中请求的真实应用程序的模型):
class Translation(models.Model):
field = models.ForeignKey(MetaField, models.CASCADE)
ref_id = models.IntegerField()
# ... other fields
class Choice(models.Model):
meta = models.ForeignKey(MetaField, on_delete=models.PROTECT)
# ... other fields
我需要一个查询来获取每个选项可用的翻译数量,其中 Translation.field_id
指的是 Choice.meta_id
,Translation.ref_id
指的是 Choice.id
。
没有外键的原因是并非所有元字段都是选择字段(例如,文本字段也可能有翻译)。我可以为每个 translatable 实体创建一个单独的 table,但是这个设置应该很容易与其中没有 group by
语句的计数子查询一起使用。
UPDATE 这是一个使用子查询的查询,应该接近您想要的结果:
str(ContentType.objects.annotate(fields=Subquery(
Field.objects.filter(model_id=OuterRef('pk')).values('model').annotate(c=Count('pk')).values('c')
)).query)
我唯一做的就是添加 values('model')
group_by 子句,这使得 Count('pk')
真正起作用,因为它将所有行聚合为一个。
当没有相关行时,它将 return null
而不是 0
,您可以使用 Coalesce
函数或 Case ... When ... then
。
使用 Django ORM 无法实现您想要的确切查询,尽管您可以使用
获得相同的结果Choice.objects.annotate(c=Count(
'meta__translation',
distinct=True,
filter=Q(meta__translation__ref_id=F('id'))
))
或者看看django-sql-utils package, as also mentioned in this post。