Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limitations of GROUP BY

Disclaimer: I'm an SQL newb and this is for a class, but I could really use a poke in the right direction.

I've got these three tables:

student(_sid_, sname, sex, age, year, gpa)
section(_dname_, _cno_, _sectno_, pname)
enroll(_sid_, grade, _dname_, _cno_, _sectno_)
(primary keys denoted by underscores)

I'm trying to write an Oracle-compatible SQL query that returns a table with the student's name (student.sname) that has the highest gpa in each section (that's including section.cno and section.sectno) as well as all the other attributes from section.

I've managed to use an aggregate query and GROUP BY to get the maximum GPA for each section:

  SELECT MAX(s.gpa), e.cno, e.sectno  
    FROM enroll e, 
         student s  
   WHERE s.sid = e.sid  
GROUP BY e.cno, e.sectno

Let alone the other section attributes, I can't even figure out how to tack on the student name (student.sname). If I add it to the SELECT clause, it has to be included in GROUP BY which messes up the rest of the query. If I use this entire query inside the WHERE or FROM clause of an outer query, I can only access the three fields in the table, which isn't that much use.

I know you can't give me the exact answer, but any hints would be appreciated!

like image 722
yavoh Avatar asked Oct 14 '22 22:10

yavoh


1 Answers

Assuming Oracle 9i+, to get only one of the students with the highest GPA (in the event of ties) use:

WITH summary AS (
   SELECT e.*,
          s.name,
          ROW_NUMBER() OVER(PARTITION BY e.cno, e.sectno
                                ORDER BY s.gpa DESC) AS rank
     FROM ENROLL e
     JOIN STUDENT s ON s.sid = e.sid)
SELECT s.*
  FROM summary s
 WHERE s.rank = 1

Non CTE equivalent:

SELECT s.*
  FROM (SELECT e.*,
               s.name,
               ROW_NUMBER() OVER(PARTITION BY e.cno, e.sectno
                                     ORDER BY s.gpa DESC) AS rank
          FROM ENROLL e
          JOIN STUDENT s ON s.sid = e.sid) s
 WHERE s.rank = 1

If you want to see all students who tied for GPA, use:

WITH summary AS (
   SELECT e.*,
          s.name,
          DENSE_RANK OVER(PARTITION BY e.cno, e.sectno
                              ORDER BY s.gpa DESC) AS rank
     FROM ENROLL e
     JOIN STUDENT s ON s.sid = e.sid)
SELECT s.*
  FROM summary s
 WHERE s.rank = 1
like image 183
OMG Ponies Avatar answered Nov 02 '22 08:11

OMG Ponies