为了保持简单,我有四个表(A,B,类别和关系),关系表在B中存储A的f :: forall r. (Bool -> ListScott Bool -> r) -> r -> r
,而类别存储B的类型。
A< --- Relation ---> B --->分类
(所以A和B之间的关系是n到n,当B和Category之间的关系是n到1时)
我需要ORM按类别和A对关系记录进行分组,然后在每个(类别,A)中计算Intensity
的{{1}}(看起来很简单直到这里),然后我想要注释Max of在每个类别中计算Sum
。
我的代码类似于:
Intensity
引发错误:
Sum
Django-group-by包含相同错误。
有关详细信息,请参阅this stackoverflow question。
我正在使用Django 2和PostgreSQL。
有没有办法使用ORM实现这一点,如果没有,使用原始SQL表达式的解决方案是什么?
经过多次努力,我发现我写的确实是一个聚合,但我想要的是找出每个类别中每个A的最大AcSum。所以我想我必须在AcSum计算后再次按结果分组。基于这种见解,我发现stack-overflow question提出了同样的概念(问题是在1年,2个月前被问到没有任何接受的答案)。 将另一个值(' id')链接到该集既不作为group_by也不作为输出属性的过滤器起作用,它从集合中删除AcSum。由于按结果集分组的更改,因此也无法将值添加到值()中。 我想我要做的是根据列内的字段(即id)重新分组查询。 有什么想法吗?
答案 0 :(得分:1)
无论是否使用ORM,都无法对聚合Max(Sum())
进行聚合,它在SQL中无效。相反,您必须将表连接到自身才能找到最大值。您可以使用子查询执行此操作。下面的代码对我来说是正确的,但请记住,我没有什么可以运行它,所以它可能不完美。
from django.db.models import Subquery, OuterRef
annotation = {
'AcSum': Sum('intensity')
}
# The basic query is on Relation grouped by A and Category, annotated
# with the Sum of intensity
query = Relation.objects.values('a', 'b__category').annotate(**annotation)
# The subquery is joined to the outerquery on the Category
sub_filter = Q(b__category=OuterRef('b__category'))
# The subquery is grouped by A and Category and annotated with the Sum
# of intensity, which is then ordered descending so that when a LIMIT 1
# is applied, you get the Max.
subquery = Relation.objects.filter(sub_filter).values('a', 'b__category').annotate(**annotation).order_by('-AcSum').values('AcSum')[:1]
query = query.annotate(max_intensity=Subquery(subquery))
这应该生成如下的SQL:
SELECT a_id, category_id,
(SELECT SUM(U0.intensity) AS AcSum
FROM RELATION U0
JOIN B U1 on U0.b_id = U1.id
WHERE U1.category_id = B.category_id
GROUP BY U0.a_id, U1.category_id
ORDER BY SUM(U0.intensity) DESC
LIMIT 1
) AS max_intensity
FROM Relation
JOIN B on Relation.b_id = B.id
GROUP BY Relation.a_id, B.category_id
通过使用诸如array_agg(Postgres)或GroupConcat(MySQL)之类的后端特定功能来收集在外部查询中组合在一起的Relation.ids,消除子查询中的连接可能更为高效。但我不知道你正在使用什么后端。
答案 1 :(得分:0)
这样的事情对你有用。我自己无法测试,所以请让我知道结果:
Relation.objects.annotate(
b_category=F('B__Category')
).values(
'A', 'b_category'
).annotate(
SumInensityPerCategory=Sum('Intensity')
).values(
'A', MaxIntensitySumPerCategory=Max('SumInensityPerCategory')
)