使用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解决方案,原始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
)
');
由于
答案 0 :(得分:3)
让我们首先确定这个SQL查询实际上是什么样的。
This DBA answer提供了对“最大n组”问题以及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说起来并不容易。
就个人而言,我甚至都不会尝试。复杂的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()
);
}
优点:您可以使用查询构建器和Eloquent抽象;允许您维护您的Ticker模型,该模型可能在请求期间需要额外的逻辑。
缺点:需要多个查询。
另一种选择可能是使用封装复杂查询的MySQL View,并创建一个单独的Eloquent模型,该模型将从该视图中获取。这样,您的应用代码可以像TickerLatest::all()
一样简单。
答案 1 :(得分:0)
您可以先获取最新的行,然后再对该集合进行分组。
$items = Ticker::latest()->get();
// exchange, base, quote
$groupedByExchange = $items->groupBy('exchange');
$groupedByBase = $items->groupBy('base');
$groupedByQoute = $items->groupBy('qoute');
更新:
您可以通过在->first()
函数后简单添加groupBy()
来获取每个组的单个项目。
$latestByExchange= $items->groupBy('exchange')->first(); // and so on
答案 2 :(得分:0)
您可以将多个参数传递给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();
答案 3 :(得分:0)
从Laravel 5.6.17开始,您可以使用joinSub(),因此可能的Eloqunish解决方案可能是这样的:
分组并找到最后一个日期的票证
$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();
答案 4 :(得分:0)
这是通过使用自左联接获得latest record per group的另一种方法,该查询可以轻松转换为laravel的查询生成器。
在普通SQL中,它可以写为
select a.*
from tickers a
left join tickers b on a.exchange = b.exchange
and a.base = b.base
and a.quote = b.quote
and a.created_at < b.created_at
where b.created_at is null
在查询生成器中,它看起来像
DB::table('tickers as a')
->select('a.*')
->leftJoin('tickers as b', function ($join) {
$join->on('a.exchange', '=', 'b.exchange')
->whereRaw(DB::raw('a.base = b.base'))
->whereRaw(DB::raw('a.quote = b.quote'))
->whereRaw(DB::raw('a.created_at < b.created_at'))
;
})
->whereNull('b.created_at')
->get();
Laravel Eloquent select all rows with max created_at
或者您使用相关子查询选择最新行
SQL
select a.*
from tickers a
where exists (
select 1
from tickers b
where a.exchange = b.exchange
and a.base = b.base
and a.quote = b.quote
group by b.exchange,b.base,b.quote
having max(b.created_at) = a.created_at
);
查询生成器
DB::table('tickers as a')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('tickers as b')
->whereRaw(DB::raw('a.exchange = b.base'))
->whereRaw(DB::raw('a.base = b.base'))
->whereRaw(DB::raw('a.quote = b.quote'))
->groupBy(['b.exchange','b.base','b.quote'])
->havingRaw('max(b.created_at) = a.created_at')
;
})
->get();