Question

I need a query in MS-Access that will return up to the last to classes each student has completed. the data looks something like this:

Students

 ID  | StudentName
 1     John
 2     Bill
 3     Ted
 4     Edward

TrainingDetails

   ID   | StudentID  | ClassName | Date
   1      1            Math        10/10/2012
   2      1            Science     12/10/2012
   3      2            Math        10/10/2012
   4      3            Math        10/10/2012
   5      2            Art         09/10/2012
   6      2            History     02/10/2012
   7      3            Science     12/10/2012
   8      3            History     02/10/2012
   9      4            Music       12/10/2012 

Desired Output

Name   | Class     | Date 
John     Science     12/10/2012
John     Math        10/10/2012
Bill     Math        10/10/2012
Bill     Art         09/10/2012
Ted      Science     12/10/2012
Ted      Math        10/10/2012
Edward   Music       12/10/2012

I've tried using the SELECT TOP 2 clause, but i only get 2 records total. I think i need some type of loop to get each student and then the top 2 records for each student, but I can't get it all in a single query.

Was it helpful?

Solution

Try the following query. I have a working fiddle with SQLServer but should work for Access. You can order the result as per your need.

SELECT s.StudentName, t.classname, t.date
FROM Students s
    INNER JOIN TrainingDetails t ON t.StudentID = s.id
WHERE t.Date in (SELECT TOP 2 t2.Date 
                 FROM TrainingDetails t2 
                 WHERE t2.StudentID = s.ID 
                 ORDER BY t2.Date DESC)

(Code corrected to work with Access SQL.)

OTHER TIPS

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top