我一直在寻找解决一个相关问题的解决方案:找到每组的最新记录,这是典型的每组n = 1的great-n-n的特殊化。
解决方案涉及您在此处处理的问题(即,如何在Eloquent中构建查询),因此我将其发布,因为它可能对其他人有帮助。它演示了使用强大的Eloquent流利接口(带有多个where
联接列和联接子选择内部的条件)的子查询构造的更简洁方法。
在我的示例中,我想获取scan_dns
每个由标识的组的最新DNS扫描结果(表)watch_id
。我分别构建子查询。
我想要Eloquent生成的SQL:
SELECT * FROM `scan_dns` AS `s`
INNER JOIN (
SELECT x.watch_id, MAX(x.last_scan_at) as last_scan
FROM `scan_dns` AS `x`
WHERE `x`.`watch_id` IN (1,2,3,4,5,42)
GROUP BY `x`.`watch_id`) AS ss
ON `s`.`watch_id` = `ss`.`watch_id` AND `s`.`last_scan_at` = `ss`.`last_scan`
我是通过以下方式做到的:
$dnsTable = (new DnsResult())->getTable();
$ids = collect([1,2,3,4,5,42]);
$subq = DnsResult::query()
->select('x.watch_id')
->selectRaw('MAX(x.last_scan_at) as last_scan')
->from($dnsTable . ' AS x')
->whereIn('x.watch_id', $ids)
->groupBy('x.watch_id');
$qqSql = $subq->toSql();
$q = DnsResult::query()
->from($dnsTable . ' AS s')
->join(
DB::raw('(' . $qqSql. ') AS ss'),
function(JoinClause $join) use ($subq) {
$join->on('s.watch_id', '=', 'ss.watch_id')
->on('s.last_scan_at', '=', 'ss.last_scan')
->addBinding($subq->getBindings());
});
$results = $q->get();
更新:
从Laravel 5.6.17开始,添加了子查询联接,因此存在一种构建查询的本地方法。
$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
->where('is_published', true)
->groupBy('user_id');
$users = DB::table('users')
->joinSub($latestPosts, 'latest_posts', function ($join) {
$join->on('users.id', '=', 'latest_posts.user_id');
})->get();
selectRaw
语句将其注入!您是否有幸将流利的查询对象而不是查询字符串传递给查询?