Джанго: запись с максимальным элементом - PullRequest
14 голосов
/ 23 марта 2012

У меня есть таблица базы данных с именем 'student', в которой есть один столбец с именем 'marks'.Я хочу студенческий рекорд с самыми высокими оценками по математике.Для этого есть простое решение: order_by()[0]:

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

Но это сортирует таблицу и затем выбирает мне первую запись.Если моя таблица огромна, это избыточно, так как мне нужна только максимальная запись.Есть ли способ просто получить наибольшее значение без сортировки?

Мне нужен весь объект, а не только максимальное значение.

Спасибо Anuj

Ответы [ 3 ]

26 голосов
/ 23 марта 2012

Требуемый SQL будет выглядеть примерно так:

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

Для этого через Django вы можете использовать API агрегации .

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

К сожалению, на самом деле это два запроса. Выполняется агрегация максимальной отметки, результат выводится в python, а затем передается во второй запрос. (Удивительно), но нет способа передать набор запросов, который является просто агрегацией без группировки, даже если это возможно. Я собираюсь открыть билет, чтобы посмотреть, как это можно исправить.

Edit:

возможно сделать это одним запросом, но это не очень очевидно. Я не видел этот метод в другом месте.

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)

Если вы напечатаете этот запрос в оболочке, вы получите:

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 (в настоящее время в альфа-версии). Это работает путем группировки аннотации по константе 1, в которую будет сгруппирована каждая строка. Затем мы убираем этот столбец группировки из списка выбора (второй values(). Джанго (сейчас) знает достаточно, чтобы определить, что группировка избыточна, и устраняет ее. Оставляя один запрос с точным SQL, который нам нужен.

0 голосов
/ 12 апреля 2012

С простой таблицей базы данных теоретически невозможно, чтобы база данных могла получить максимальное значение для вас без предварительной сортировки.Подумайте об этом, как база данных может узнать, какое максимальное значение, если она не просматривает каждую строку?

Конечно, это с очень наивной настройкой.К счастью, у вас есть две опции:

  1. использовать индекс.Если вы создаете индекс для этого столбца, сортировка обычно может использовать его в своих интересах, сохраняя при этом полное сканирование таблицы.

  2. normalize (он же предварительный расчет).Создайте где-нибудь другую таблицу, в которой будет храниться максимальное значение, и проверяйте / обновляйте ее каждый раз, когда объект Student добавляется / изменяется / удаляется.

Не зная дополнительных требований, яНастоятельно рекомендуем использовать индекс.

Проверить: https://docs.djangoproject.com/en/dev/ref/models/fields/#db-index

0 голосов
/ 23 марта 2012

Этот вопрос может быть вам полезен: Как сделать SELECT MAX в Django?

Просто используйте агрегацию.

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

Не проверено, но должно работать. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...