If we use a self-join query we can assign a rank to the various classes for each student (1 = most recent):
SELECT
t1.StudentID,
t1.ClassName,
t1.Date,
COUNT(*) AS Rank
FROM
TrainingDetails AS t1
INNER JOIN
TrainingDetails AS t2
ON t2.StudentID = t1.StudentID
AND t2.Date >= t1.Date
GROUP BY
t1.StudentID,
t1.ClassName,
t1.Date
ORDER BY 1, 4
returning
StudentID ClassName Date Rank
--------- --------- ---------- ----
1 Science 2012-12-10 1
1 Math 2012-10-10 2
2 Math 2012-10-10 1
2 Art 2012-09-10 2
2 History 2012-02-10 3
3 Science 2012-12-10 1
3 Math 2012-10-10 2
3 History 2012-02-10 3
4 Music 2012-12-10 1
We can join that with [Students] to get the name, and limit the results to the ones WHERE Rank <= 2
:
SELECT
s.StudentName,
cr.ClassName,
cr.Date,
cr.Rank
FROM
Students AS s
INNER JOIN
(
SELECT
t1.StudentID,
t1.ClassName,
t1.Date,
COUNT(*) AS Rank
FROM
TrainingDetails AS t1
INNER JOIN
TrainingDetails AS t2
ON t2.StudentID = t1.StudentID
AND t2.Date >= t1.Date
GROUP BY
t1.StudentID,
t1.ClassName,
t1.Date
) AS cr
ON cr.StudentID = s.ID
WHERE cr.Rank <= 2
ORDER BY s.ID, cr.Rank
returning
StudentName ClassName Date Rank
----------- --------- ---------- ----
John Science 2012-12-10 1
John Math 2012-10-10 2
Bill Math 2012-10-10 1
Bill Art 2012-09-10 2
Ted Science 2012-12-10 1
Ted Math 2012-10-10 2
Edward Music 2012-12-10 1