Możesz używać funkcji analitycznych
SELECT *
FROM (SELECT c.*,
rank() over (partition by user_id order by ts desc) rnk
FROM comments c)
WHERE rnk = 1
W zależności od tego, jak chcesz obsłużyć powiązania (jeśli mogą istnieć dwa wiersze z tym samym user_id i ts ), możesz użyć row_number lub dense_rank funkcja zamiast rank . rank pozwoliłoby, aby wiele rzędów było pierwszymi, gdyby był remis. row_number arbitralnie zwróciłby jeden wiersz, gdyby był remis. dense_rank zachowywałby się jak rank dla rzędów, które remisowały na pierwszym miejscu, ale uważałyby, że następny rząd jest drugim, a nie trzecim, zakładając, że dwa rzędy są remisujące dla pierwszego.