-- like this
SELECT
s.course_id, s.course_name,
roll_no = MIN(s.roll_no),
student_name = CASE WHEN g.SetSize = 1 THEN MIN(s.student_name) ELSE 'Default' END,
s.geog_marks, s.math_marks
FROM #students s
INNER JOIN (
SELECT course_id, geog_marks, math_marks, SetSize = COUNT(*)
FROM #students
GROUP BY course_id, geog_marks, math_marks
) g
ON g.course_id = s.course_id
AND g.geog_marks = s.geog_marks
AND g.math_marks = s.math_marks
GROUP BY s.course_id, s.course_name, s.geog_marks, s.math_marks, g.SetSize
HAVING (CASE WHEN g.SetSize = 1 THEN MIN(s.student_name) ELSE 'Default' END) = 'Default'
ORDER BY s.course_id, roll_no
-- or run the result set into a temporary table:
IF object_id('TempDB..#Results') IS NOT NULL
DROP TABLE #Results
SELECT
s.course_id, s.course_name,
roll_no = MIN(s.roll_no),
student_name = CASE WHEN g.SetSize = 1 THEN MIN(s.student_name) ELSE 'Default' END,
s.geog_marks, s.math_marks
INTO #Results
FROM #students s
INNER JOIN (
SELECT course_id, geog_marks, math_marks, SetSize = COUNT(*)
FROM #students
GROUP BY course_id, geog_marks, math_marks
) g
ON g.course_id = s.course_id
AND g.geog_marks = s.geog_marks
AND g.math_marks = s.math_marks
GROUP BY s.course_id, s.course_name, s.geog_marks, s.math_marks, g.SetSize
ORDER BY s.course_id, roll_no
SELECT *
FROM #Results
SELECT *
FROM #Results
WHERE student_name = 'Default'
SELECT *
FROM #Results
WHERE student_name <> 'Default'
For fast, accurate and documented assistance in answering your questions, please read this article.
Understanding and using APPLY, (I) and (II) Paul White
Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden