Laravel eloquent получить последние строки сгруппированных строк

Laravel eloquent получить последние строки сгруппированных строк

24.02.2018 05:28:52 Просмотров 40 Источник

Используя Eloquent, пытаясь найти способ получить последние строки каждой строки, сгруппированные по: exchange, base, quote

Данные

exchange    base    quote   price   value   created_at

bittrex     BTC     USD     10000   10000   2018-01-05
bittrex     BTC     USD     9000    9000    2018-01-01
poloniex    BTC     USD     10001   10001   2018-01-05
poloniex    BTC     USD     9000    9000    2018-01-01
binance     BTC     USD     10002   10002   2018-01-05
binance     BTC     USD     9000    9000    2018-01-01
binance     ETH     USD     800     800     2018-01-05
binance     ETH     USD     700     700     2018-01-01

Результат:

bittrex     BTC     USD     10000   10000   2018-01-05
poloniex    BTC     USD     10001   10001   2018-01-05
binance     BTC     USD     10002   10002   2018-01-05
binance     ETH     USD     800     800     2018-01-05

ОБНОВЛЕНИЕ

Я пошел с решением @Cryode, raw SQL вместо красноречивого (если кто-то может придумать один красноречивый запрос для репликации результатов запроса ниже, не стесняйтесь публиковать).

Я также изменил структуру таблицы, чтобы добавить id(инкременты) в качестве первичного ключа. Я также добавил следующий индекс $table->index(['exchange', 'base', 'quote', 'created_at']);

Вот решение этой проблемы:

$currencies  = DB::select('SELECT *
                             FROM (
                                    SELECT DISTINCT exchange, base, quote
                                      FROM tickers
                                  ) AS t1
                             JOIN tickers
                               ON tickers.id =
                                 (
                                    SELECT id
                                      FROM tickers AS t2
                                     WHERE t2.exchange  = t1.exchange
                                       AND t2.base      = t1.base
                                       AND t2.quote     = t1.quote
                                     ORDER BY created_at DESC
                                     LIMIT 1
                                 )
                         ');

Спасибо

У вопроса есть решение - Посмотреть?

https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows#comment84925030_48958737
что-то вроде Ticker::distinct ()->orderBy('created_at', 'asc')->>groupBy('exchange') - >>>get(['exchange']); это позволит получить различные обмены...я чувствую, что это близко к решению.
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows#comment84925523_48958737
Вы created_at включает в себя время, а также, верно?
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows#comment84955058_48958737
Да и время тоже

Ответы - Laravel eloquent получить последние строки сгруппированных строк / Laravel eloquent get the latest rows of grouped rows

Dexter Bengil

24.02.2018 10:23:52

Вы можете сначала извлечь последние строки, а затем сгруппировать коллекцию позже.

$items = Ticker::latest()->get();
// exchange, base, quote
$groupedByExchange = $items->groupBy('exchange');
$groupedByBase = $items->groupBy('base');
$groupedByQoute = $items->groupBy('qoute');

ОБНОВЛЕНИЕ: Вы можете получить один элемент каждой группы простым добавлением ->first()after функции groupBy().

$latestByExchange= Ticker::latest()->groupBy('exchange')->first(); // and so on
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48960420#comment84955460_48960420
Спасибо последнее будет заказывать desc, но можно группировать обмен, базу, котировку и иметь одно последнее значение
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48960420#comment84960949_48960420
Спасибо, getting "разрешенный размер памяти 134217728 байт исчерпан" с ошибкой " Ticker:: latest () - >get ()", хотя таблица слишком велика для сортировки.. хм.. около 2м строк. Придется проверить это
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48960420#comment104940851_48960420
Это не самая лучшая идея, так как она вернет каждую строку из базы данных для данной модели, следовательно, ваша ошибка памяти.
Hassan Jamal

24.02.2018 11:09:06

Вы можете передать несколько аргументов методу groupBy для группировки по нескольким столбцам

Пожалуйста, обратитесь к документации https://laravel.com/docs/5.6/queries#ordering-grouping-limit-and-offset

$users = DB::table('users')
            ->groupBy('first_name', 'status')
            ->having('account_id', '>', 100)
            ->get();
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48960751#comment84955355_48960751
Я могу легко группировать, но тогда, как можно выбрать самую последнюю строку из каждой возвращенной группы
Является ответом!
Aken Roberts

25.02.2018 09:50:52

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

Этот ответ DBA дает некоторое представление о проблеме" greatest-n-per-group", а также примеры PostgreSQL и MySQL. Вдохновленный этим ответом, вот что я придумал для вашей единственной таблицы (предполагая, что MySQL является вашей базой данных):

SELECT ticker.*
  FROM (
    SELECT DISTINCT exchange, base, quote
      FROM ticker
  ) AS exchanges
  JOIN ticker
    ON ticker.id = 
       (
         SELECT id
           FROM ticker
          WHERE ticker.exchange = exchanges.exchange
            AND ticker.base = exchanges.base
            AND ticker.quote = exchanges.quote
       ORDER BY created_at DESC
          LIMIT 1
       );

О боже. Получить это в Laravel-speak не кажется легким.

Лично я бы даже не пытался. Сложные SQL-запросы - это просто потому, что они используют вашу базу данных для создания отчетов, сбора данных и т. д. Попытка засунуть это в конструктор запросов утомительна и, вероятно, не приносит никакой пользы.

Тем не менее, если вы хотите достичь того же результата простым способом, используя конструктор запросов Laravel и Eloquent, вот вариант:

// Get the unique sets of tickers we need to fetch.
$exchanges = DB::table('ticker')
    ->select('exchange, base, quote')
    ->distinct()
    ->get();

// Create an empty collection to hold our latest ticker rows,
// because we're going to fetch them one at a time. This could be
// an array or however you want to hold the results.
$latest = new Collection();

foreach ($exchanges as $exchange) {
    $latest->add(
        // Find each group's latest row using Eloquent + standard modifiers.
        Ticker::where([
                'exchange' => $exchange->exchange,
                'base' => $exchange->base,
                'quote' => $exchange->quote,
            ])
            ->latest()
            ->first()
    );
}

Плюсы: вы можете использовать конструктор запросов и красноречивые абстракции; позволяет поддерживать вашу модель тикера, которая может иметь дополнительную логику, необходимую во время запроса.

Минусы: требуется несколько запросов.


Другим вариантом может быть использование представления MySQL, которое инкапсулирует сложный запрос, и создание отдельной красноречивой модели, которая будет извлекаться из этого представления. Таким образом, код вашего приложения может быть таким же простым, как TickerLatest::all().

https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48971014#comment84955319_48971014
Спасибо за вашу помощь, я пытаюсь проверить MySQL выше, но так как первый Select не извлекает id, а только exchange, base_currency, quote_currency, я не могу присоединиться к id. Опция foreachхороша, но это ударило бы по db сотни раз при каждом вызове. Будем разбираться во всем этом. Я предполагаю, что использование Eloquent будет исключено, и я должен использовать необработанный запрос
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48971014#comment84955499_48971014
oups означало baseи quote, а не base_currencyи quote_currency
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48971014#comment84958781_48971014
У вас нет столбца idв этой таблице?
https://stackoverflow.com/questions/48958737/laravel-eloquent-get-the-latest-rows-of-grouped-rows/48971014#comment84961016_48971014
У вас должен быть один не для производительности, а для того, чтобы сделать дополнительные запросы (например, этот) возможными / более легкими. Если вы хотите повысить производительность, вы можете нормализовать повторяющиеся данные, добавить индексы (см. so Q&A I linked for index recommendations) и т. д. Первичный ключ также позволит масштабировать MySQL в будущем.
jannej

15.11.2018 05:14:43

Поскольку Laravel 5.6.17 вы можете использовать joinSub (), поэтому возможное красноречивое решение может быть чем-то вроде этого:

Сгруппируйте и найдите билет с последней датой

    $latest = Ticker::select('exchange', 'base', 'quote', DB::raw('MAX(created_at) as created_at'))
        ->groupBy('exchange', 'base', 'quote');

И присоединиться к последней из каждой группы снова все записи с joinSub()

    $posts = DB::table('tickets')
        ->joinSub($latest, 'latest_tickets', function ($join) {
            $join->on('tickets.exchange', '=', 'latest_tickets.exchange')
                 ->on('tickets.base', '=', 'latest_tickets.base')
                 ->on('tickets.quote', '=', 'latest_tickets.quote')
                  ->on('tickets.created_at', '=', 'latest_posts. created_at');
        })->get();
Закрыть X