Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: Record with max element

I have a database table named 'student' in which there is one column named 'marks'. I want the student record with highest marks in Maths. There is a simple solution to it using order_by()[0]:

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

But this sorts the table and then fetches me the first record. If my table is huge, this is redundant as I need only the max record. Is there any way to just get the largest value without sorting?

I want the whole object, not just the max value.

Thanks Anuj

like image 605
Anuj Avatar asked Mar 23 '12 11:03

Anuj


People also ask

How to get the Max Marks of a class in Django?

SELECT * FROM STUDENT WHERE marks = (SELECT MAX(marks) FROM STUDENT) To do this via Django, you can use the aggregation API. max_marks = Student.objects.filter( subject='Maths' ).aggregate(maxmarks=Max('marks'))['maxmarks'] Student.objects.filter(subject='Maths', marks=max_marks) Unfortunately, this query is actually two queries.

How to find the max/min of a rating in Django?

I've tested this for my project, it finds the max/min in O (n) time: from django.db.models import Max # Find the maximum value of the rating and then get the record with that rating. # Notice the double underscores in rating__max max_rating = App.objects.aggregate (Max ('rating')) ['rating__max'] return App.objects.get (rating=max_rating)

How to get get count/average/min/max values from model field in Django?

Get count, average, min, max values from model field using Django Aggregate. Django queries help to create, retrieve, update and delete objects. But sometimes we need to get summered values from the objects. Then a Simple solution is to use Django aggregate feature Here are simple examples of how to use aggregation. app/models.py.

How to get the latest value of a field in Django?

You need to specify a field in latest (). eg. Or if your model’s Meta specifies get_latest_by, you can leave off the field_name argument to earliest () or latest (). Django will use the field specified in get_latest_by by default. Show activity on this post. Show activity on this post. Show activity on this post.


1 Answers

The SQL required would be something like this:

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

To do this via Django, you can use the aggregation API.

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

Unfortunately, this query is actually two queries. The max mark aggregation is executed, the result pulled into python, then passed to the second query. There's (surprisingly) no way to pass a queryset that's just an aggregation without a grouping, even though it should be possible to do. I'm going to open a ticket to see how that might be fixed.

Edit:

It is possible to do this with a single query, but it's not very obvious. I haven't seen this method elsewhere.

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)

If you print this query in the shell you get:

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))

Tested on Django 1.11 (currently in alpha). This works by grouping the annotation by the constant 1, which every row will group into. We then strip this grouping column from the select list (the second values(). Django (now) knows enough to determine that the grouping is redundant, and eliminates it. Leaving a single query with the exact SQL we needed.

like image 82
Josh Smeaton Avatar answered Oct 09 '22 23:10

Josh Smeaton