Search code examples
sqlaggregate-functions

Does `ORDER BY` work with aggregate functions?


need to select top students from college by average mark on a subject. (there are 2 tables: students: id, name, subjects: id, name, marks: : id, student_id, subject_id, val)

The query I made:

select s.id, sb.id, avg(m.val) as avgmark
from marks m
join students s on s.id = m.student_id
join subjects sb on sb.id = m.subject_id
where sb.name = 'math' and avgmark > 80
GROUP BY student_id, subject_id
ORDER BY avgmark DESC

doesn't work AFAIK because of aggregate functions work after where clause works.

What is the reasonable way to perfom it?
Do I need to use having somehow?
Does ORDER BY work with aggregate functions?


Solution

  • This is not about ORDER BY. You aggregate the joined rows in order to end up with one row per student and subject ID. WHERE happens before aggregation. In WHERE we look at the original rows, not at aggregation results. If you want to look at an aggregation result like the average, then you must place the condition in the HAVING clause instead.

    This query gets you all students with an average grade over 80, showing the top students first:

    select m.student_id, m.subject_id, avg(m.val) as avgmark
    from marks m
    join students s on s.id = m.student_id
    join subjects sb on sb.id = m.subject_id
    where sb.name = 'math'
    group by m.student_id, m.subject_id
    having avg(m.val) > 80
    order by avgmark desc;
    

    The students table is superflouous by the way and the subjetcs table only needed for a lookup, so I'd write:

    select m.student_id, m.subject_id, avg(m.val) as avgmark
    from marks m
    where m.subject_id = (select sb.id from subjects sb where sb.name = 'math')
    group by m.student_id, m.subject_id
    having avg(m.val) > 80
    order by avgmark desc;