Вы можете получить строки, которые вам нужны, только с помощью оконных функций:
select tr.*
from (select tr.*,
max(tr.dated) filter (where tr.grade <> tr.last_grade) over (partition by tr.studentID, tr.testID) as max_other_grade_date
from (select tr.*,
first_value(tr.grade) over (partition by tr.studentID, tr.testID order by tr.dated desc) as last_grade
from testresult tr
) tr
) tr
where max_other_grade_date is null or dated > max_other_grade_date ;
Простая агрегация получает желаемые дни:
select tr.studentID, tr.testID,
max(dated) - min(dated)
from (select tr.*,
max(tr.dated) filter (where tr.grade <> tr.last_grade) over (partition by tr.studentID, tr.testID) as max_other_grade_date
from (select tr.*,
first_value(tr.grade) over (partition by tr.studentID, tr.testID order by tr.dated desc) as last_grade
from testresult tr
) tr
) tr
where max_other_grade_date is null or dated > max_other_grade_date
group by tr.studentID, tr.testID;
Здесь - это скрипта SQL.