2012-03-23 54 views
12

我有一個名爲'student'的數據庫表,其中有一列名爲'marks'。我想要數學中最高分的學生記錄。有一個簡單的解決方案是使用order_by()[0]到:Django:使用最大元素的記錄

Student.objects.filter(subject='Maths').order_by('-marks')[0] 

但這種排序表,然後取出我的第一個記錄。如果我的表格很大,這是多餘的,因爲我只需要最大記錄。有沒有辦法在沒有排序的情況下獲得最大的價值?

我想要整個對象,而不僅僅是最大值。

感謝Anuj

回答

22

所需的SQL是這樣的:

SELECT * 
FROM STUDENT 
WHERE marks = (SELECT MAX(marks) FROM STUDENT) 

要通過Django中做到這一點,你可以使用aggregation API

max_marks = Student.objects.filter(
    subject='Maths' 
).aggregate(maxmarks=Max('marks'))['maxmarks'] 
Student.objects.filter(subject='Maths', marks=max_marks) 

不幸的是,這個查詢實際上是兩個查詢。最大標記聚合被執行,結果被拉入python,然後傳遞給第二個查詢。有(令人驚訝的)沒有辦法傳遞一個查詢集,這只是一個沒有分組的聚合,儘管它應該可以做到。我打算打開一張票,看看如何解決這個問題。

編輯:

可以使用單個查詢做到這一點,但不是很明顯。我在其他地方沒有看到這種方法。

from django.db.models import Value 

max_marks = (
    Student.objects 
      .filter(subject='Maths') 
      .annotate(common=Value(1)) 
      .values('common') 
      .annotate(max_marks=Max('marks')) 
      .values('max_marks') 
) 

Student.objects.filter(subject='Maths', marks=max_marks) 

如果您在shell打印此查詢你:

SELECT 
     "scratch_student"."id", 
     "scratch_student"."name", 
     "scratch_student"."subject", 
     "scratch_student"."marks" 
    FROM "scratch_student" 
WHERE ( 
     "scratch_student"."subject" = Maths 
    AND "scratch_student"."marks" = (
     SELECT 
       MAX(U0."marks") AS "max_marks" 
     FROM "scratch_student" U0 
     WHERE U0."subject" = Maths)) 

測試在Django的1.11(目前處於alpha)。這是通過將常數1分組標註進行分組的,每一行都將分組。然後我們從選擇列表中去掉這個分組列(第二個values()。Django(現在)足夠了解這個分組是多餘的,並且消除了這個分組。留下一個具有我們需要的確切SQL的單個查詢

+0

什麼是F在F( 'max_mark')? – 2013-01-16 07:59:31

+0

@ChadVernon,它們被稱爲F()表達式。它們允許您使用另一列的值。 https://docs.djangoproject.com/en/dev/topics/db/queries/#query-expressions – 2013-01-16 21:08:45

+2

這不起作用。生成的SQL如下所示:'SELECT *,MAX(「mark」)AS「max_mark」FROM STUDENT HAVING「STUDENT」。「mark」=(MAX(「STUDENT」。「marks」))',學生們。 – 2016-11-18 19:55:06

2

這個問題可以是有益的給你: How to do SELECT MAX in Django?

只需使用聚集。

from django.db.models import Max 
Student.objects.filter(subject='Math').aggregate(Max('marks')) 

未測試,但應該工作。 :)

0

With a天真的數據庫表,理論上沒有可能的方式,數據庫可以在沒有首先排序的情況下爲你檢索最大值,只要想一想,數據庫如何知道哪個是最大值,除非它查看每一行?幸運的是,你有兩種選擇:幸運的是你有兩種選擇:

  1. 使用索引。如果您在該列上創建索引,則排序通常可以利用索引 - 爲您節省全表掃描。

  2. 正常化(又名預計算)。在存儲最大值的地方創建另一個表,並確保每次添加/修改/刪除一個Student對象時檢查/更新它。

不知道更多的要求,我強烈建議使用索引。

退房:https://docs.djangoproject.com/en/dev/ref/models/fields/#db-index

+0

這個問題似乎更多的是關於Django的ORM,而不是真正的數據庫或表結構,你的答案是面向的。 – TheCatParty 2015-10-14 19:04:44

+1

理論上,找到最大值是一個o(n)操作 - 您只需訪問每個元素一次,這與排序不同。對於排序,您需要進行o(nlogn)比較。 – abhaga 2015-11-23 10:37:54

+0

@abhaga在我看來,數據庫的主要目標是允許快速訪問數據。通過創建索引,數據庫保持排序,以便訪問最大值(或最小值或偏移量)是O(1)操作。 – 2016-08-08 14:58:12