The hard part is getting the zeros to show up for students that do not have any classes in a given session.
This is a job for a PARTITION
outer join.
select c.studentname,
s.classsession,
round(ratio_to_report(count(c.classsession))
over ( partition by c.studentname),2) pct
from c partition by ( studentname )
right outer join ( SELECT distinct classsession from c ) s
on s.classsession = c.classsession
group by c.studentname, s.classsession
order by c.studentname, s.classsession;
Note the PARTITION
keyword in the join. This tells Oracle to perform the outer join for each partition. So, if a given studentname
does not have a classsession
, add it for that student.
Also, ratio_to_report
is a good function for calculating percentages.
Here is a full example, with data:
with c (ClassSession, StudentName) AS (
SELECT 'Evening', 'Ben' FROM DUAL UNION ALL
SELECT 'Morning', 'Chris' FROM DUAL UNION ALL
SELECT 'Afternoon', 'Roger' FROM DUAL UNION ALL
SELECT 'Evening', 'Ben' FROM DUAL UNION ALL
SELECT 'Afternoon', 'Ben' FROM DUAL UNION ALL
SELECT 'Morning', 'Roger' FROM DUAL UNION ALL
SELECT 'Morning', 'Ben' FROM DUAL UNION ALL
SELECT 'Afternoon', 'Chris' FROM DUAL)
select c.studentname,
s.classsession,
round(ratio_to_report(count(c.classsession))
over ( partition by c.studentname),2) pct
from c partition by ( studentname )
right outer join ( SELECT distinct classsession from c ) s on s.classsession = c.classsession
group by c.studentname, s.classsession
order by c.studentname, s.classsession;
╔══════════════════════════════════════════════════════════════════╗
║ STUDENTNAME CLASSSESSION PCT ║
╠══════════════════════════════════════════════════════════════════╣
║ ----------- ------------ -------------------------------------- ║
║ Ben Afternoon 0.25 ║
║ Ben Evening 0.5 ║
║ Ben Morning 0.25 ║
║ Chris Afternoon 0.5 ║
║ Chris Evening 0 ║
║ Chris Morning 0.5 ║
║ Roger Afternoon 0.5 ║
║ Roger Evening 0 ║
║ Roger Morning 0.5 ║
╚══════════════════════════════════════════════════════════════════╝