O problema do "último por grupo" é extremamente comum em SQL. Existem inúmeros exemplos de soluções para este problema apenas neste site.
Se seus timestamps são únicos por atividade de membro:
SELECT
m.id,
m.name,
a.code activity_code,
a.timestamp activity_timestamp,
a.description activity_description
FROM
members m
INNER JOIN activities a ON a.member_id = m.id
WHERE
a.timestamp = (SELECT MAX(timestamp) FROM activities WHERE member_id = m.id)
alternativamente, se o ID da sua atividade estiver aumentando monotonicamente com o tempo:
...
WHERE
a.id = (SELECT MAX(id) FROM activities WHERE member_id = m.id)
Você não precisa agrupar. Mas a consulta se beneficiará de um índice em
activities
sobre (member_id, timestamp)
ou (member_id, id)
, respectivamente. EDITAR
Para mostrar os membros que não registraram uma atividade, use uma entrada à esquerda como esta.
SELECT
m.id,
m.name,
a.code activity_code,
a.timestamp activity_timestamp,
a.description activity_description
FROM
members m
LEFT JOIN activities a ON
a.member_id = m.id
AND a.timestamp = (SELECT MAX(timestamp) FROM activities WHERE member_id = m.id)
Observe que não há
WHERE
cláusula. Semanticamente, WHERE é aplicado depois as junções são feitas. Portanto, uma cláusula WHERE removeria as linhas que o LEFT JOIN adicionou, efetivamente fornecendo o mesmo resultado que o INNER JOIN original. Mas se você aplicar o predicado adicional diretamente na condição de junção, o LEFT JOIN funcionará conforme o esperado.