Попробуйте это:
select messageId,
messageBody,
messageSentAt,
isRead
from (
select messageId,
messageBody,
messageSentAt,
isRead,
row_number() over (partition by senderId, receiverId order by messageSentAt desc) rn
from (
select messageId,
case when senderId > receiverId then senderId else receiverId end senderId,
case when senderId < receiverId then senderId else receiverId end receiverId,
messageBody,
messageSentAt,
isRead
from Messages
) a
) a where rn = 1
Самый внутренний запрос объединяет отправителей и получателей, поэтому их порядок не имеет значения:
case when senderId > receiverId then senderId else receiverId end senderId,
case when senderId < receiverId then senderId else receiverId end receiverId,
Промежуточному запросу присваивается номер строки для каждой строки, поэтому первая - самая последняя дата. Большинство внешних запросов просто фильтруются на основе этого номера строки.