文章转发自专业的Laravel开发者社区,原始链接: https://learnku.com/laravel/t...
如果你一直关注我的工作,你就会知道我推崇将我们的Laravel应用里面的工作更多地放到数据库层。 通过在数据库层完成更多工作,我们通常可以减少我们制造的数据库查询的数量,减少应用程序使用的内存量,并减少 Eloquent 处理模型所需的时间。 这可以带来一些非常显着的性能提升。
将更多工作推到数据库层的一种很好的方法是使用子查询。 子查询允许您在另一个数据库查询中运行嵌套查询。 当无法通过关系查询查到结果时,这是获取辅助模型数据的强大方法,而无需进行任何额外的数据库查询。 您还可以在 order by
语句, where
语句和其他数据库子句中使用子查询。
在 Laracon US 2019 talk期间, 我参考了我正在使用的查询构造器,这让我们在 laravel 中使用子查询变得更加简单. 我已经向 laravel 提交了三个 pr ,将这些添加到 laravel 的内核中.
概述:
“Select” 子查询
Pull request #29567 select()
和addSelect()
查询构建器方法支持子查询
举个例子,假设我们有一个目的地表 destinations
和一个到目的地的航班表 flights
。flights
表包含一个 arrival_at
字段,表示航班何时到达目的地。
在 Laravel 6.0 中使用了新的子查询功能,比如要查询全部目的地 destinations
,以及抵达各目的地最后一班飞机的信息 name
,我们可以用单条语句这样查询:
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
注意这里是怎么使用 Eloquent 来生成的子查询。这样的语法更好,更具有直观表现力。同样的,你也可以使用 query builder:
return Destination::addSelect(['last_flight' => function ($query) {
$query->select('name')
->from('flights')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1);
}])->get();
“Order by” 子查询
此外, Pull request #29563 使我们可以在查询生成器的 orderBy()
方法中使用子查询。继续我们上面的示例,我们可以根据最后一班航班到达目的地的时间对目的地进行排序。
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
与选择一样,您也可以直接使用查询构建器来创建子查询。 例如,您可能希望根据用户的上次登录日期订购:
return User::orderBy(function ($query) {
$query->select('created_at')
->from('logins')
->whereColumn('user_id', 'users.id')
->latest()
->limit(1);
})->get();
“From” 子查询
最后, Pull request #29602 使我们在查询构造器中的 from()
使用子查询成为可能。这些有时称为派生表。
例如,你可能想要计算应用程序中用户的平均捐赠总额。 但是,在SQL中,嵌套聚合函数是不可能的:
AVG(SUM(amount))
相反,我们可以使用from子查询来计算它:
return DB::table(function ($query) {
$query->selectRaw('sum(amount) as total')
->from('donations')
->groupBy('user_id');
}, 'donations')->avg('total');
你可能不需要每天都用到这个,但是当你确实需要它的时候,它是必不可少的。
如果您在Laravel
之外使用Eloquent
,那么需要注意的一个突破性变化,在Illuminate/Database/Capsule/Manager
对象上 table()
方法有明显的改变。它已从表 table($table, $connection = null)
更改为表table($table, $as = null, $connection = null)
。
了解更多
如果您有兴趣了解更多关于子查询和其他高级数据库技术的信息,请务必关注我的博客,并观看我的 Laracon US 2019 talk演讲。
在Laracon,我还发布了一个新的视频课程,我正在研究Eloquent的性能模式。本课程的目的是教Laravel开发人员如何把更多的工作推送到数据库层来大幅提高Laravel应用程序的性能,同时仍然使用Eloquent ORM。如果你感兴趣的话,一定要加入邮件列表!