Python Django使用表达式聚合查询

Python Django使用表达式聚合查询,python,sql,django,django-1.3,Python,Sql,Django,Django 1.3,我有一个模型XYZ,我需要获得给定查询集的字段a、b和表达式x/y的最大值 它适用于野外作业。比如: >>> XYZ.all().aggregate(Max('a')) ... {'a__max': 10} >>> XYZ.all().aggregate(Max('x/y')) >>> XYZ.all().aggregate(Max(F('x')/F('y'))) XYZ.all().extra(select={'z':'x/y'})

我有一个模型XYZ,我需要获得给定查询集的字段a、b和表达式x/y的最大值

它适用于野外作业。比如:

>>> XYZ.all().aggregate(Max('a'))

... {'a__max': 10}
>>> XYZ.all().aggregate(Max('x/y'))
>>> XYZ.all().aggregate(Max(F('x')/F('y')))
XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z'))
但是,我找不到一种方法来处理表达式。尝试类似于:

>>> XYZ.all().aggregate(Max('a'))

... {'a__max': 10}
>>> XYZ.all().aggregate(Max('x/y'))
>>> XYZ.all().aggregate(Max(F('x')/F('y')))
XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z'))
给出一个错误:

*** FieldError: Cannot resolve keyword 'x/y' into field. Choices are: a, b, x, y, id
*** AttributeError: 'ExpressionNode' object has no attribute 'split'
尝试类似于:

>>> XYZ.all().aggregate(Max('a'))

... {'a__max': 10}
>>> XYZ.all().aggregate(Max('x/y'))
>>> XYZ.all().aggregate(Max(F('x')/F('y')))
XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z'))
给出一个错误:

*** FieldError: Cannot resolve keyword 'x/y' into field. Choices are: a, b, x, y, id
*** AttributeError: 'ExpressionNode' object has no attribute 'split'
甚至像:

>>> XYZ.all().aggregate(Max('a'))

... {'a__max': 10}
>>> XYZ.all().aggregate(Max('x/y'))
>>> XYZ.all().aggregate(Max(F('x')/F('y')))
XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z'))
也不起作用,并给出与上述相同的错误:

FieldError: Cannot resolve keyword 'z' into field. Choices are: a, b, x, y, id
我发现的一个黑客就是:

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z
这实际上是可行的,因为它生成了正确的SQL,但令人困惑的是,我确实在z attribute中得到了正确的值,但没有得到正确的实例,即具有该最大值的实例

当然,我也可以将原始查询或技巧与extra和order_by一起使用,但Django以一种很好的方式支持聚合查询,但即使使用自己的F表达式也不能支持表达式,这对我来说真的没有意义


有什么办法吗?

我想你应该分别得到最大值

result = XYZ.aggregate(Max('x'), Max('y'))
然后将这两个字段分开

result['x__max'] \ result['y__max']

在SQL中,您需要的实际上是

SELECT x/y, * FROM XYZ ORDER BY x/y DESC LIMIT 1;
# Or more verbose version of the #1
SELECT x/y, id, a, b, x, y FROM XYZ GROUP BY x/y, id, a, b, x, y ORDER BY x/y DESC LIMIT 1;
# Or
SELECT * FROM XYZ WHERE x/y = (SELECT MAX(x/y) FROM XYZ) LIMIT 1;
因此,在Django ORM中:

XYZ.objects.extra(select={'z':'x/y'}).order_by('-z')[0]
# Or
XYZ.objects.extra(select={'z':'x/y'}).annotate().order_by('-z')[0]
# Or x/y=z => x=y*z
XYZ.objects.filter(x=models.F('y') * XYZ.objects.extra(select={'z':'MAX(x/y)'})[0].z)[0]
版本

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z
没有正确的x、y和instance,因为在没有GROUP BY的情况下,在所有行中计算MAX函数,因此返回的QuerySet中的所有实例的z值都将与MAXx/y值相同。

使用F对象的示例应该可以正常工作,因为Django 1.8:

XYZ.all().aggregate(Max(F('x')/F('y')))
下面有一个片段演示了在中使用Sum和F对象进行聚合:


对于低于1.8的版本,您可以通过这种未记录的方式实现相同的功能

Book.objects.all().aggregate(price_per_page=Sum('price_per_page', 
                                                field='book_price/book_pages'))
这适用于Postgres,我不知道MySQL


来源:

这没有任何意义。即使它返回带有max x和y对的行,也不一定是max x/y。例如,Maxx/Maxy行是69/16=4,而Maxx/y是8/1=8Right,但目的是获取最大值本身,正如XYZ.all.aggregateMax'a'返回的值,而不是包含它的实例。带有额外选择的版本最接近它。不返回正确的实例是一个令人困惑的副作用,但它返回正确的值。正如我在开场白中所说的,我知道有额外的和order_by的解决方案,但这些方案是不可接受的,因为它们需要一个完整的表格,而不是一次通过。Django用单个字段而不是表达式来支持Max aggregate没有多大意义。@pjwerneck您调用的令人困惑的副作用的原因,在我的答案的最后一段中描述。如果您只想要最大值,XYZ.objects.extraselect={'z':'MAXx/y'}[0]。z就足够了,没有顺序。甚至可以直接使用光标。执行“从XYZ选择最大X/y”。我同意您的看法,Django不提供聚合w/表达式,因为它可能比支持单字段IMO困难得多。您可能有兴趣了解在聚合中使用F对象的能力。很高兴知道。谢谢