Подтвердить что ты не робот

Django: запись с максимальным элементом

У меня есть таблица базы данных с именем "student", в которой есть один столбец с именем "метки". Я хочу, чтобы студент записывался с высшими отметками в Maths. Это простое решение с помощью order_by()[0]:

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

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

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

Спасибо Anuj

4b9b3361

Ответ 1

Требуемый 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()). Django (сейчас) знает достаточно, чтобы определить, что группировка избыточна и исключает ее. Оставляя единственный запрос с точным SQL, который нам нужен.

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

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

Отъезд: https://docs.djangoproject.com/en/dev/ref/models/fields/#db-index