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
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.
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)
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With