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

Здравствуйте! Я пытаюсь сделать выборку всех последних сообщений, по одному от каждого пользователя, но не могу разобраться, как это реализовать. Вот структура моей базы данных: <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 <br/> В левой части - все users <br/> В правой части - их messages <br/> Сопоставление таблиц по user_id <br/> Получается таблица всех сообщений всех пользователей. <br/> Немного модифицируем запрос -  messages сортируем по message_id в порядке убывания и берём только первую запись (через LIMIT) <br/> В итоге имеем то, что Вы и хотели - список всех пользователей с их последним сообщением. <br/> PS Чистый SQL не привожу, т.к. предварительно потребуется его отладка. Но при понимании SQL составить запрос для Вас будет не проблема.
В общем ключе вот так - <br/> <pre><code class="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</code></pre> <br/> <br/> ЧатГПТ Laravel, заметьте, что первую часть запроса в любом случае выполнять через Query Builder, но можно вывести ее в получение конкретных id последних сообщений. И дальше двумя, можно отдельными запросами уже через модель - получить данные по сообщениям и пользователям. <br/> <pre><code class="php">// Подзапрос для получения maxId и from_id
$subQuery = (new Query())
    -&gt;select(['maxId' =&gt; 'MAX(id)', 'from_id'])
    -&gt;from('messages')
    -&gt;where(['from_id' =&gt; [1, 2, 3]])
    -&gt;groupBy('from_id')
    -&gt;orderBy(['maxId' =&gt; SORT_DESC]);

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

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