I think it's actually even simpler.
SELECT StudentID, Programme, Min([Year]) AS [Year]
FROM #Students
GROUP BY StudentID, Programme
HAVING Min([Year]) = 2009
You may not even need the HAVING clause depending on what you are looking for.
ROW_NUMBER() is useful if you want to return fields that are neither used for the grouping nor for determining the Top/Bottom/First/Last record. That's not the situation here based solely on your sample data. You're grouping on two of the fields and using the remaining one for determining the first record, so you can use a simple GROUP BY.
This has the added benefit that it only scans the table once instead of twice.
Drew
J. Drew Allen
Business Intelligence Analyst
Philadelphia, PA