Как получить первые и последние сообщения пользователей?

Здравствуйте! Я пытаюсь сделать выборку всех последних сообщений, по одному от каждого пользователя, но не могу разобраться, как это реализовать. Вот структура моей базы данных: <br/> <br/> users['id', 'name', 'password'] <br/> <br/> messages['id', 'from_id', 'to_id', 'message']
  • 12 января 2025 г. 12:53
Ответы на вопрос 3
Чтобы получить последние сообщения от каждого пользователя, вам нужно сделать выборку из таблицы сообщений с использованием группировки по пользователю и получения последнего сообщения. В SQL это можно сделать с помощью подзапросов или функции `ROW_NUMBER()`. Вот пример, как это можно реализовать:

### Используя `ROW_NUMBER()`

Если ваша база данных поддерживает оконные функции (например, PostgreSQL, MySQL 8.0+, SQL Server и др.), вы можете использовать следующий запрос:

```sql
SELECT m.*
FROM (
    SELECT id, from_id, to_id, message,
           ROW_NUMBER() OVER (PARTITION BY from_id ORDER BY id DESC) as rn
    FROM messages
) m
WHERE rn = 1;
```

Этот запрос:

1. Определяет номер строки (`ROW_NUMBER()`) для каждого сообщения, группируя их по `from_id` и упорядочивая по `id` в порядке убывания (последние сообщения будут первыми).
2. Внешний запрос выбирает только те сообщения, которые имеют номер строки 1, что соответствует последнему сообщению от каждого пользователя.

### Используя подзапрос

Если ваш SQL-диалект не поддерживает оконные функции, вы можете использовать подзапрос:

```sql
SELECT m.*
FROM messages m
JOIN (
    SELECT from_id, MAX(id) as max_id
    FROM messages
    GROUP BY from_id
) m_max ON m.id = m_max.max_id;
```

В этом запросе:

1. Внутренний запрос находит максимальный `id` (последнее сообщение) для каждого `from_id`.
2. Внешний запрос соединяет результат с таблицей `messages`, чтобы получить все поля сообщения, соответствующие найденному `max_id`.

### Объединение с таблицей пользователей

Если вам также нужны данные о пользователях, вы можете объединить результаты с таблицей пользователей:

```sql
SELECT u.name, m.message
FROM (
    SELECT m.*
    FROM messages m
    JOIN (
        SELECT from_id, MAX(id) as max_id
        FROM messages
        GROUP BY from_id
    ) m_max ON m.id = m_max.max_id
) m
JOIN users u ON m.from_id = u.id;
```

Этот запрос вернёт имена пользователей и их последние сообщения. Вы можете изменить выборку, если вам нужны другие поля.
Через LEFT JOIN 
В левой части - все users
В правой части - их messages
Сопоставление таблиц по user_id
Получается таблица всех сообщений всех пользователей.
Немного модифицируем запрос - messages сортируем по message_id в порядке убывания и берём только первую запись (через LIMIT)
В итоге имеем то, что Вы и хотели - список всех пользователей с их последним сообщением.
PS Чистый SQL не привожу, т.к. предварительно потребуется его отладка. Но при понимании SQL составить запрос для Вас будет не проблема.
В общем ключе вот так - 
SELECT * FROM
(SELECT max(id) as maxId, from_id 
GROUP BY from_id
WHERE from_id in (1,2,3)
ORDER BY maxId desc
) as t1
INNER JOIN messages m on t1.maxId = m.id


ЧатГПТ Laravel, заметьте, что первую часть запроса в любом случае выполнять через Query Builder, но можно вывести ее в получение конкретных id последних сообщений. И дальше двумя, можно отдельными запросами уже через модель - получить данные по сообщениям и пользователям.
// Подзапрос для получения maxId и from_id
$subQuery = (new Query())
    ->select(['maxId' => 'MAX(id)', 'from_id'])
    ->from('messages')
    ->where(['from_id' => [1, 2, 3]])
    ->groupBy('from_id')
    ->orderBy(['maxId' => SORT_DESC]);

// Основной запрос с INNER JOIN
$query = (new Query())
    ->select('*')
    ->from(['t1' => $subQuery])
    ->innerJoin('messages m', 't1.maxId = m.id');

// Выполнение запроса
$results = $query->all();
Похожие вопросы