Django: Record with max element

后端 未结 3 1697
慢半拍i
慢半拍i 2020-12-03 10:58

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

3条回答
  •  -上瘾入骨i
    2020-12-03 11:32

    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.

提交回复
热议问题