编程

18条优化Laravel 数据库查询的建议(7-9)

1503 2021-12-25 01:02:39

7. 不要加载belongsTo 关联模型,如果你需要的只有它的id

假定你有两张表 postsauthors。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,得到同样的结果。