Laravel eloquent 获取分组行的最新行

use*_*502 3 greatest-n-per-group laravel eloquent laravel-5

使用 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
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

更新

我选择了@Cryode 解决方案,原始 SQL 而不是 Eloquent(如果有人可以提出一个 Eloquent 查询来复制下面查询的结果,请随时发布)。

我还更改了表的结构以添加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
                                 )
                         ');
Run Code Online (Sandbox Code Playgroud)

谢谢

Ake*_*rts 5

让我们首先确定这个 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
       );
Run Code Online (Sandbox Code Playgroud)

哦亲爱的。将其引入 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()
    );
}
Run Code Online (Sandbox Code Playgroud)

优点:您可以使用查询构建器和 Eloquent 抽象;允许您维护您的 Ticker 模型,该模型可能在请求期间需要额外的逻辑。

缺点:需要多次查询。


另一种选择是使用封装复杂查询的MySQL 视图,并创建一个单独的 Eloquent 模型,该模型将从该视图中获取。这样,您的应用程序代码就可以像TickerLatest::all().