【发布时间】:2017-07-21 12:04:56
【问题描述】:
这是一个前沿功能,我目前非常关注它并很快就被淘汰了。我想将子查询聚合注释到现有查询集上。在 1.11 之前执行此操作意味着自定义 SQL 或锤击数据库。 Here's the documentation for this,以及它的例子:
from django.db.models import OuterRef, Subquery, Sum
comments = Comment.objects.filter(post=OuterRef('pk')).values('post')
total_comments = comments.annotate(total=Sum('length')).values('total')
Post.objects.filter(length__gt=Subquery(total_comments))
他们在聚合上注释,这对我来说似乎很奇怪,但无论如何。
我正在为此苦苦挣扎,所以我将其归结为我拥有数据的最简单的真实示例。我有Carparks,其中包含许多Spaces。使用Book→Author,如果这让你更快乐,但是——现在——我只想用Subquery*注释相关模型的计数。
spaces = Space.objects.filter(carpark=OuterRef('pk')).values('carpark')
count_spaces = spaces.annotate(c=Count('*')).values('c')
Carpark.objects.annotate(space_count=Subquery(count_spaces))
这给了我一个可爱的ProgrammingError: more than one row returned by a subquery used as an expression,在我的脑海中,这个错误非常有意义。子查询返回带有注释总数的空格列表。
这个例子表明某种魔法会发生,我最终会得到一个我可以使用的数字。但这不是在这里发生的吗?如何对聚合子查询数据进行注释?
嗯,正在向我的查询的 SQL 添加一些内容...
我建立了一个新的停车场/空间模型,它工作正常。所以下一步是找出是什么毒害了我的 SQL。在 Laurent 的建议下,我查看了 SQL 并试图使其更像他们在答案中发布的版本。这就是我发现真正问题的地方:
SELECT "bookings_carpark".*, (SELECT COUNT(U0."id") AS "c"
FROM "bookings_space" U0
WHERE U0."carpark_id" = ("bookings_carpark"."id")
GROUP BY U0."carpark_id", U0."space"
)
AS "space_count" FROM "bookings_carpark";
我已经突出显示了它,但它是那个子查询的GROUP BY ... U0."space"。出于某种原因,它正在重新调整两者。调查仍在继续。
编辑 2:好的,只需查看子查询 SQL,我可以通过☹看到第二组
In [12]: print(Space.objects_standard.filter().values('carpark').annotate(c=Count('*')).values('c').query)
SELECT COUNT(*) AS "c" FROM "bookings_space" GROUP BY "bookings_space"."carpark_id", "bookings_space"."space" ORDER BY "bookings_space"."carpark_id" ASC, "bookings_space"."space" ASC
编辑 3:好的!这两种模型都有排序顺序。这些正在执行到子查询。正是这些命令使我的查询膨胀并破坏了它。
我猜这可能是 Django 中的一个错误,但没有删除这两个模型上的 Meta-order_by,有什么方法可以在查询时取消排序查询?
*我知道我可以为这个例子添加一个 Count 注释。我使用它的真正目的是一个更复杂的过滤器计数,但我什至无法让它工作。
【问题讨论】:
-
嗯,目前的答案表明这是可能的。我的模型中可能有一些我没有考虑过的东西会影响我的这个。我会在星期一完成它,如果需要的话,我会从零开始构建它,看看它在哪里崩溃。
-
我认为你不应该在
.annotate之前使用values,因为这会创建grouping。你可以试试这个queryset:Carpark.objects.annotate(space_count=Subquery(Space.objects.filter(carpark=OuterRef('pk')).annotate(space_cnt=Count('pk')).values('space_cnt'))) -
感谢 Todor,但仍会引发“用作表达式的子查询返回的不止一行”。我将重建模型以确保我的项目中没有任何干扰。
-
@Oli 在您的第二次编辑中,您似乎正在使用自定义管理器 (
objects_standard)。这是您的额外分组的来源吗?另外,bookings_space上的space属性是什么? -
如果您不希望对查询应用任何排序,甚至不希望使用默认排序,请在不带参数的情况下调用
order_by()。
标签: django django-aggregation django-annotate django-subquery