Это самая большая проблема для каждой группы, которая часто возникает при переполнении стека.
Вот мой обычный ответ:
select
p.name player,
s.date first_score,
s.points points
from players p
join scores s
on s.player_id = p.id
left outer join scores s2
on s2.player_id = p.id
and s2.date < s.date
where
s2.player_id is null
;
Другими словами, учитывая количество баллов s, попробуйте найти количество баллов s2 для того же игрока, но с более ранней датой. Если ранняя оценка не найдена, то s - самая ранняя.
Ваш комментарий о связях: у вас должна быть политика, которую следует использовать в случае ничьей. Одна возможность - если вы используете автоинкрементные первичные ключи, то с наименьшим значением будет более ранний. См. Дополнительный термин во внешнем соединении ниже:
select
p.name player,
s.date first_score,
s.points points
from players p
join scores s
on s.player_id = p.id
left outer join scores s2
on s2.player_id = p.id
and (s2.date < s.date or s2.date = s.date and s2.id < s.id)
where
s2.player_id is null
;
По сути, вам нужно добавлять термины разрешения конфликтов, пока вы не дойдете до столбца, который гарантированно будет уникальным, по крайней мере, для данного игрока. Первичный ключ таблицы часто является лучшим решением, но я видел случаи, когда подходил другой столбец.
Что касается комментариев, которыми я поделился с @OMG Ponies, помните, что этот тип запросов чрезвычайно выигрывает от правильного индекса.