Получить последний ряд с группой по и Laravel

Несмотря на то, что есть несколько подобных вопросов, я не могу получить свой запрос, чтобы вернуть строку с самой последней датой с группой по.

У меня есть следующая таблица..

| message_id | from | to | created_at | status
----------------------------------------------
|    1       |   1  |  2 | 2017-04-06 |   1
|    2       |   1  |  2 | 2017-04-07 |   0
|    3       |   3  |  4 | 2017-04-06 |   1
|    4       |   3  |  4 | 2017-04-07 |   0
----------------------------------------------

и я пытаюсь получить строки с самой последней датой.

| message_id | from | to | created_at | status
----------------------------------------------
|    2       |   1  |  2 | 2017-04-07 |   0
|    4       |   3  |  4 | 2017-04-07 |   0

В настоящее время этот запрос возвращает строки с последней последней датой.

$messages = Message::where('to', Auth::id())
                    ->groupBy('from')
                    ->orderBy('created_at', 'DESC')
                    ->paginate(10);

6 ответов

Решение

Проблема в том, что набор результатов будет сначала сгруппирован, а затем упорядочен. Вы можете использовать вложенный выбор, чтобы получить то, что вы хотите.

SQL-запрос:

SELECT t.* FROM (SELECT * FROM messages ORDER BY created_at DESC) t GROUP BY t.from

С Laravel:

$messages = Message::select(DB::raw('t.*'))
            ->from(DB::raw('(SELECT * FROM messages ORDER BY created_at DESC) t'))
            ->groupBy('t.from')
            ->get();

Вам просто нужно добавить свой where() статьи.

Если кто-то все еще ищет краткий и красноречивый ответ на использование groupBy() с участием latest() просто добавь unique() после get() нравится...

      $unreadMessages = Message::where('receiver_id', Auth::user()->id)
    ->where('read_at', null)
    ->latest()
    ->get()
    ->unique('user_id');

Чтобы получить самую последнюю запись для каждого from вы можете использовать самостоятельное присоединение

DB::table('message as m')
  ->select('m.*')
  ->leftJoin('message as m1', function ($join) {
        $join->on('m.from', '=', 'm1.from')
             ->whereRaw(DB::raw('m.created_at < m1.created_at'));
   })
  ->whereNull('m1.from')
  ->orderBy('m.created_at', 'DESC')
  ->paginate(10);

В SQL это будет выглядеть так:

select m.*
from message m
left join message m1 on m.from = m1.from
and m.created_at < m1.created_at
where m1.from is null
order by m.created_at desc

Laravel Eloquent выбирает все строки с max created_at

Вы можете заменить groupBy отличным, как это работает в моем случае.

$messages = Message::where('to', Auth::id())
                ->orderBy('created_at', 'DESC')
                ->distinct('from')
                ->paginate(10);

Надеюсь это поможет.

В вашем примере у вас есть поле даты, а не поле даты и времени, поэтому мой предпочтительный метод заключается в следующем:

# Mysql
select * from
  your_table
where date_field = (select max(date_field) from your_table)
// Laravel
YourModel:::query()->whereRaw('date_field = (select max(date_field) from your_table)')->get();
  • Обратите внимание, что вышеприведенное не будет работать должным образом для полей даты и времени, поскольку строки будут иметь разные временные метки, скорее всего, она просто возвратит одну последнюю строку.

Вы также можете захотеть orderBymessage_id также

$messages = Message::where('to', Auth::id())
                    ->groupBy('from')
                    ->orderBy('created_at', 'DESC')
                    ->orderBy('message_id', 'DESC')
                    ->paginate(10);
Другие вопросы по тегам