Dal momento che MySQL non supporta le funzioni della finestra, non credo che ci sia un modo per farlo senza una sottoquery:
SELECT f.id AS forum_id,
f.name AS forum_name,
t.id AS thread_id,
t.topic AS thread_topic,
t.ts AS thread_timestamp,
p.id AS post_id,
p.content AS post_content,
p.ts AS post_timestamp
FROM forums f
JOIN (SELECT t2.forum_id, max(p2.ts) as ts
FROM posts p2
JOIN threads t2 ON p2.thread_id = t2.id
GROUP BY t2.forum_id) max_p ON f.id = max_p.forum_id
JOIN posts p ON max_p.ts = p.ts
JOIN threads t ON f.id = t.forum_id AND p.thread_id = t.id
ORDER BY p.ts
Naturalmente, la memorizzazione nella cache degli ultimi risultati ti permetterebbe di farlo senza la penalizzazione delle prestazioni della chiamata a MAX(), ma con gli indici giusti, questo non dovrebbe essere un grosso problema...
AGGIORNAMENTO
Il modo più conciso per includere i thread senza post e i forum senza thread sarebbe utilizzare LEFT JOIN invece di INNER JOIN:
SELECT f.id AS forum_id,
f.name AS forum_name,
t.id AS thread_id,
t.topic AS thread_topic,
t.ts AS thread_timestamp,
p.id AS post_id,
p.content AS post_content,
p.ts AS post_timestamp
FROM forums f
LEFT JOIN (SELECT t2.forum_id, max(COALESCE(p2.ts, t2.ts)) as ts, COUNT(p2.ts) as post_count
FROM threads t2
LEFT JOIN posts p2 ON p2.thread_id = t2.id
GROUP BY t2.forum_id) max_p ON f.id = max_p.forum_id
LEFT JOIN posts p ON max_p.ts = p.ts
LEFT JOIN threads t ON f.id = t.forum_id AND (max_p.post_count = 0 OR p.thread_id = t.id)
ORDER BY p.ts