如何优化大查询?

How to optimize a big query?

我有一个有 10000 个用户的 table 并且与 work_hours table.

有关系

这是我的用户模型以及我如何获得工作时间总和。

protected $appends = ['sum_work_hours'];

public function work_hours()
{
    return $this->hasMany(WorkHour::class);
}

public function getSumWorkHoursAttribute()
{
    return $this->work_hours->sum('hours_total');
}

我做了一个查询来显示所有用户和他们的工作时间总和,如下所示:

public function getHoursDatatable()
{
    $users = User::with('work_hours')->get();

    return Datatables::of($users)->make();
}

并且此查询大约需要 7-8 秒 来显示 table,这非常慢。

我知道这是因为对每个用户 work_hours table 的总工作时间进行了总结,我想了解如何优化此查询,如果考虑到数量,这甚至是可能的数据?

更新:

    $users = User::with('work_hours')->select(['users.name', 'users.email']);

    return Datatables::of($users)
        ->add_column('sum_work_hours', function(User $user) {
            return $user->work_hours->sum('hours_total');
        })
        ->make();

这给了我一个错误:

DataTables warning: table id=users-table - Ajax error. For more information about this error, please see http://datatables.net/tn/7

更新 2

这个有效:

public function getHoursDatatable()
{
    $users = User::with('work_hours');

    return Datatables::of($users)
        ->addColumn('sum_work_hours', function(User $user) {
            return $user->work_hours->sum('hours_total');
        })
        ->make();
}

但加载大约需要 7-8 秒...

在某些时候,您的数据会变得太大而无法一次查询所有内容。

基本上你有两个选择:

1。 由于您已经有 10K 用户(而且这个数字可能还会增长?)我建议切换到异步(serverSide: true 选项)。您正在使用数据tables,所以显然您不需要在一个页面中显示所有 10K 个用户,也不需要所有这些用户用于单个报告。

Datatables 确实允许服务器端处理,它确实允许分页,同时保留过滤和排序选项,就像您现在可能正在做的那样。

yajra 包将继续做它现在正在做的事情,你必须改变的是你没有将所有用户分配给视图并像那样构建它,你需要一个空 table 并让前端填充它。只需创建一个 api 端点,您可以将数据 table 指向该端点,以便从中获取分页列表。

因此,不要在您的视图中使用 @foreach ($users as $user),不要在显示 table 的控制器操作中获取它,让 api 端点处理它。

示例:

<table id="example" class="display" cellspacing="0" width="100%">
    <thead>
        <tr>
            <th>First name</th>
            <th>Last name</th>
            <th>Total Hours</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <th>First name</th>
            <th>Last name</th>
            <th>Total Hours</th>
        </tr>
    </tfoot>
</table>

并且您需要执行此操作(在此处或您正在使用的某些库中)

$(document).ready(function() {
    $('#example').DataTable( {
        "processing": true,
        "serverSide": true,
        "ajax": "../server_side/scripts/server_processing.php"
    } );
} );

您还需要:

//code.jquery.com/jquery-1.12.4.js https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js

如果您尚未将其包含在您的项目中,那么您的信息中并不清楚。

有关详细信息,请参阅:Datatables Server-side processing

2。 将总小时数存储在其他地方。当对 Workhour 模型的更改完成后,您可以简单地更新每个用户的总小时数。这样您就不需要在每次请求单个或列表(或所有)用户时都进行求和。

我的建议是至少做#1,然后#2 会是一个很好的优化。