如何优化大查询?

时间:2017-09-03 20:28:19

标签: laravel datatables yajra-datatable

我有一个包含10000个用户的表,并且与work_hours表有关系。

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

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秒才能显示非常慢的表格。

我知道这是因为总结了work_hours表中每个用户的总工作时间,我想学习如何在考虑数据量的情况下优化此查询?

更新

    $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警告:table id = users-table - Ajax错误。更多   有关此错误的信息,请参阅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 个答案:

答案 0 :(得分:1)

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

基本上你有两个选择:

1。 既然你已经拥有10K用户(并且这个数字可能还会增长?)我建议切换到异步(serverSide: true选项)。您正在使用数据表,因此显然您不需要1页中的所有10K用户,也不需要所有这些用户来处理单个报告。

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

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

因此,不要在视图中使用@foreach ($users as $user),不要在显示表的控制器操作中获取它,让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将是一个很好的优化之后。