18条优化Laravel 数据库查询的建议(7-9)
7. 不要加载belongsTo 关联模型,如果你需要的只有它的id
假定你有两张表 posts
和 authors
。Posts 表有一个字段 author_id
与 authors 表有 belongsTo 关联。
获取 post 的 author id, 你可能会这么做:
$post = Post::findOrFail(<post id>);
$post->author->id;
这样会执行两条查询:
select * from posts where id = <post id> limit 1
select * from authors where id = <post author id> limit 1
好的方法是,直接如下获取 author_id
:
$post = Post::findOrFail(<post id>);
$post->author_id; // posts table has a column author_id which stores id of the author
什么时候采用此方式?
如果你能确定 post 表中提到的数据在 authors 表中总是存在数据
8. 避免非必要的查询
我们经常会查询非必要的数据,参考下例:
<?php
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
$private_posts = PrivatePost::all();
return view('posts.index', ['posts' => $posts, 'private_posts' => $private_posts ]);
}
}
以上示例从两个不同的表中查询数据(ex: posts
, private_posts
) 并传到试图 VIEW 中。
视图文件如下:
// posts/index.blade.php
@if( request()->user()->isAdmin() )
<h2>Private Posts</h2>
<ul>
@foreach($private_posts as $post)
<li>
<h3>{{ $post->title }}</h3>
<p>Published At: {{ $post->published_at }}</p>
</li>
@endforeach
</ul>
@endif
<h2>Posts</h2>
<ul>
@foreach($posts as $post)
<li>
<h3>{{ $post->title }}</h3>
<p>Published At: {{ $post->published_at }}</p>
</li>
@endforeach
</ul>
上例中,你会发现 $private_posts
变量只有在用户是管理员 时才可见,其余用户看不到这些文章。
问题在于我们做下列操作时,
$posts = Post::all();
$private_posts = PrivatePost::all();
我们做了两次查询。一次从 post 表读取记录,一次从 private_post 表获取记录。
private_post 表中的数据直对管理员可见。即便他们不可见我们仍然做了查询。我们可以修改逻辑避免额外查询:
$posts = Post::all();
$private_posts = collect();
if( request()->user()->isAdmin() ){
$private_posts = PrivatePost::all();
}
通过修改以上逻辑,我们对管理员做了两次查询,其他用户则只做一次。
9. 合并类似查询
有时,我们会对同一张表做处不同的查询:
$published_posts = Post::where('status','=','published')->get();
$featured_posts = Post::where('status','=','featured')->get();
$scheduled_posts = Post::where('status','=','scheduled')->get();
以上代码从同一张表中查询不同状态的数据,这些代码会生成以下查询语句
select * from posts where status = 'published'
select * from posts where status = 'featured'
select * from posts where status = 'scheduled'
如你所见,它生成了 3 条不同的查询语句。我们可以重构代码只做一次查询。
$posts = Post::whereIn('status',['published', 'featured', 'scheduled'])->get();
$published_posts = $posts->where('status','=','published');
$featured_posts = $posts->where('status','=','featured');
$scheduled_posts = $posts->where('status','=','scheduled');
select * from posts where status in ( 'published', 'featured', 'scheduled' )
以上代码将所有指定的状态合并成一次查询,然后按照每个状态筛选分别创建各自 collection,得到同样的结果。