Showing posts with label gpa. Show all posts
Showing posts with label gpa. Show all posts

2015-09-08

Query for Student Transcript (and Cumulative GPA)

There are some rules to generate transcript, some universities will use bet the highest grade, but some other will use get the latest grade. For example if we have this schema

CREATE TABLE schedules (
  id BIGSERIAL PRIMARY KEY,
  subject_id BIGINT FOREIGN KEY REFERENCES subjects(id),
  semester VARCHAR(5) -- YYYYS -- year and semester
);
CREATE TABLE enrollments (
  id BIGSERIAL PRIMARY KEY,
  student_id BIGINT FOREIGN KEY REFERENCES students(id),
  schedule_id BIGINT FOREIGN KEY REFERENCES schedules(id),
  grade VARCHAR(2) -- letter
);

If the university rule for transcript is get the highest score, the query is quite simple, first you'll need to make sure that grade is ordered in certain way on MAX agregate (for example: A+ > A > A- > B+ > ...) , then you'll need to do this query:

SELECT s.subject_id
  , MAX(e.grade)
FROM enrollments e
  JOIN schedules s
    ON e.schedule_id = s.id
WHERE e.student_id = ?
GROUP BY 1

If the university rules is get the latest score, first of all, you'll need to make sure that the semester is ordered (for example: 2015A < 2015B < 2015C < 2015A < ...), then you'll need to find the latest semester of each subject, for example:

SELECT s.subject_id
  , MAX(s.semester)
FROM enrollments e
  JOIN schedules s
    ON e.schedule_id = s.id
WHERE e.student_id = ?
GROUP BY 1

Then you'll need to fetch the credits that is the latest semester, for example:

SELECT s.subject_id
  , e.grade
FROM enrollments e
  JOIN schedules s
    ON e.schedule_id = s.id
WHERE e.student_id = ?
  AND (s.subject_id, s.semester) IN
    ( SELECT s.subject_id
        , MAX(s.semester)
      FROM enrollments e
        JOIN schedules s
          ON e.schedule_id = s.id
      WHERE e.student_id = ?
      GROUP BY 1
    )
GROUP BY 1

That way, it you'll only get the latest grade (as defined on the sub-query).

There are another alternative for this, you can use aggregate with OVER and PARTITION BY syntax, then compare the result with the aggregate to remove the previous scores, for example:

SELECT r.subject_id
  , r.grade
FROM (
  SELECT s.subject_id
    , e.grade
    , s.semester
    , MAX(s.semester) OVER (PARTITION BY s.subject_id) max_sem
  FROM enrollments e
    JOIN schedules s
      ON e.schedule_id = s.id
  WHERE e.student_id = ?) r
WHERE r.semester_id = r.max_sem

That's it, that's the way to get the transcript. Those queries can be modified to fetch the Cumulative GPA. just add one more join with subjects table to get the units/credits, then just calculate the SUM(subjects.unit * enrollments.num_grade) / SUM(subjects.unit)
You can also calculate the Cumulative GPA per semester by adding the semester for the partition/group by part. If you wonder, why do I write this post? because I have googled and not found any posts that write about this.