2012-04-19 136 views
7

我有一個模型XYZ,我需要爲給定的查詢集獲取字段a,b和表達式x/y的最大值。Django使用表達式聚合查詢

它的作品非常適合領域。例如:

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

... {'a__max': 10} 

但是,我找不到表達式的方法。想是這樣的:

>>> XYZ.all().aggregate(Max('x/y')) 

給出了一個錯誤:

*** FieldError: Cannot resolve keyword 'x/y' into field. Choices are: a, b, x, y, id 

想是這樣的:

>>> XYZ.all().aggregate(Max(F('x')/F('y'))) 

給出了一個錯誤:

*** AttributeError: 'ExpressionNode' object has no attribute 'split' 

即使是這樣的:

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 atttribute處獲得了正確的值,但卻不是正確的實例,即具有最大值的那個。

當然,我也可以用extra()和order_by()來使用原始查詢或技巧,但對於我來說Django以一種很好的方式支持聚合查詢的方式確實沒有意義,但即使使用自己的F表達式也不能支持表達式。

有沒有辦法做到這一點?

+0

你可能有興趣知道,能夠使用'F()'在聚集的對象是[即將Django的1.8版本的一部分(https://code.djangoproject.com/維基/ Version1.8Roadmap)。 – 2015-02-16 04:34:48

回答

6

在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和實例,因爲MAX功能的所有行中進行評估,當沒有GROUP BY,從而在返回的查詢集所有實例將具有相同的價值z作爲MAX(x/y)

+0

正確,但目的是獲取最大值本身作爲回報來自XYZ.all()。aggregate(Max('a')),而不是包含它的實例。額外選擇的版本最接近它。不返回正確的實例是一個令人困惑的副作用,但它返回正確的值。正如我在開場白中所說的,我知道帶有extra和order_by的解決方案,但是這些都是不可接受的,因爲它們需要一個完整的表格,而不是一個單一的通道。 Django支持具有單個字段而不是表達式的Max聚合沒有多大意義。 – 2012-04-19 15:25:05

+0

@pjwerneck您所說的副作用令人困惑的原因在我的答案的最後一段中描述。如果你只想要最大值,'XYZ.objects.extra(select = {'z':'MAX(x/y)'})[0] .z'就足夠了,沒有order_by。甚至直接'cursor.execute('XYZ'中的'SELECT MAX(x/y)')'。我同意你/ Django不提供聚合w /表達式,因爲它可能比支持單個域的IMO要困難得多。 – okm 2012-04-19 15:56:08

-3

我想你應該分別得到最大的價值

result = XYZ.aggregate(Max('x'), Max('y')) 

再劃分這兩個領域

result['x__max'] \ result['y__max'] 
+0

這沒有任何意義。即使它返回具有最大x和y對的行,也不一定是最大x/y。例如,最大(x)/最大(y)行是69/16 = 4,而最大(x/y)是8/1 = 8 – 2012-04-19 12:27:05

2

你使用F()對象的例子應該從Django 1開始正常工作。8:

XYZ.all().aggregate(Max(F('x')/F('y'))) 

有演示聚集與Sum()F()對象在Django aggregation cheat sheet片段:

Book.objects.all().aggregate(price_per_page=Sum(F('price')/F('pages')) 
+0

很高興知道。謝謝! – 2016-01-06 03:48:14