18条优化Laravel 数据库查询的建议(4-6)
4. Count 查询使用 query 而非 collection
计算数据库表的记录数,我们可能会这么做:
$posts = Post::all()->count(); //When using eloquent
$posts = DB::table('posts')->get()->count(); //When using query builder
这会产生以下查询
select * from posts
以上查询会先查询所有记录,将他们加载到 collection 对象中,再计算结果。如果记录少还好,随着表中记录增加,我们可能会耗尽内存。
因此,应该直接在数据库中计算总数:
$posts = Post::count(); //When using eloquent
$posts = DB::table('posts')->count(); //When using query builder
生成如下查询语句
select count(*) from posts
在 sql 中计算行数是慢处理过程,当数据库吉林市很多时表现不佳。所以需要尽量避免计算行数。
5. 通过渴求式加载避免 N+1 查询
你可能听过很多次这条建议。因此,长话短说。假设有以下场景:
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
return view('posts.index', ['posts' => $posts ]);
}
}
// posts/index.blade.php file
@foreach($posts as $post)
<li>
<h3>{{ $post->title }}</h3>
<p>Author: {{ $post->author->name }}</p>
</li>
@endforeach
以上代码查询所有记录,在网页中显示标题和作者。假设你的 post 模型有一个关联模型 author。执行以上代码会返回以下查询:
select * from posts // Assume this query returned 5 posts
select * from authors where id = { post1.author_id }
select * from authors where id = { post2.author_id }
select * from authors where id = { post3.author_id }
select * from authors where id = { post4.author_id }
select * from authors where id = { post5.author_id }
如你所见,我们用一个查询来查询 posts 还有 5 个查询用来查询a uthors(假设有 5个 posts)。因此每个 post 会单独作出一个查询来查找作者。
因此如果有 N 个 post, 会产生 N+1 次查询(1 次 post, N 次查 post 的作者 author)。这就是常见的 N+1 查询问题。要避免这个,通过如下方式渴求式(eager load)加载:
$posts = Post::all(); // Avoid doing this
$posts = Post::with(['author'])->get(); // Do this instead
执行以上代码会生成以下查询
select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
6. 渴求加载嵌入式关联模型
接上例,假设 author 隶属于某个团队 team, 你同时要显示团队名称,试图如下:
@foreach($posts as $post)
<li>
<h3>{{ $post->title }}</h3>
<p>Author: {{ $post->author->name }}</p>
<p>Author's Team: {{ $post->author->team->name }}</p>
</li>
@endforeach
执行以下代码
$posts = Post::with(['author'])->get();
会产生以下查询:
select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id = { author1.team_id }
select * from teams where id = { author2.team_id }
select * from teams where id = { author3.team_id }
select * from teams where id = { author4.team_id }
select * from teams where id = { author5.team_id }
如你所见,即便你渴求式加载关联模型 authors,仍然会在 team 上有多的查询。我们可以按照以下方式解决:
$posts = Post::with(['author.team'])->get();
执行以上代码生成查询语句:
select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id in( { author1.team_id }, { author2.team_id }, { author3.team_id }, { author4.team_id }, { author5.team_id } )
通过渴求式加载嵌入式关联模型,我们可以将查询总数从 11 减少到 3。