我们正在使用LUMEN开发API。 今天我们在收集“TimeLog”模型时遇到了一个混乱的问题。 我们只想从板模型和任务模型中获取所有时间日志以及其他信息。 在一行时间日志中,我们有一个board_id和一个task_id。两者都是1:1的关系。
这是我们获取整个数据的第一个代码。这花费了很多时间,有时我们会超时: 的 BillingController.php
public function byYear() {
$timeLog = TimeLog::get();
$resp = array();
foreach($timeLog->toArray() as $key => $value) {
if(($timeLog[$key]->board_id && $timeLog[$key]->task_id) > 0 ) {
array_push($resp, array(
'board_title' => isset($timeLog[$key]->board->title) ? $timeLog[$key]->board->title : null,
'task_title' => isset($timeLog[$key]->task->title) ? $timeLog[$key]->task->title : null,
'id' => $timeLog[$key]->id
));
}
}
return response()->json($resp);
}
已经建立关系的 TimeLog.php 。
public function board()
{
return $this->belongsTo('App\Board', 'board_id', 'id');
}
public function task()
{
return $this->belongsTo('App\Task', 'task_id', 'id');
}
我们的新方式是这样的: 的 BillingController.php
public function byYear() {
$timeLog = TimeLog::
join('oc_boards', 'oc_boards.id', '=', 'oc_time_logs.board_id')
->join('oc_tasks', 'oc_tasks.id', '=', 'oc_time_logs.task_id')
->join('oc_users', 'oc_users.id', '=', 'oc_time_logs.user_id')
->select('oc_boards.title AS board_title', 'oc_tasks.title AS task_title','oc_time_logs.id','oc_time_logs.time_used_sec','oc_users.id AS user_id')
->getQuery()
->get();
return response()->json($timeLog);
}
我们删除了TimeLog.php中的关系,因为我们不再需要它了。现在我们有一个加载时间大约1秒,这很好! 时间日志表中有大约20k个条目。
我的问题是:
如果您需要更多信息,请问我。
答案 0 :(得分:2)
- 第一个问题 -
您可能面临的一个问题是在内存中存储了大量数据,即:
$timeLog = TimeLog::get();
这已经是巨大的了。然后,当您尝试将集合转换为数组时:
$timeLog->toArray()
效率不高(虽然我可能不完全正确)所以我建议使用五个方法(一个可以节省数百个查询的方法),最后一个方法可以有效地将结果返回到自定义:
由于你有很多数据,然后chunk
结果为:Laravel chunk所以你改为:
$timeLog = TimeLog::chunk(1000, function($logs){
foreach ($logs as $log) {
// Do the stuff here
}
});
其他方式是使用游标(只运行条件匹配的一个查询)游标的内部操作,正如所理解的那样使用Generators。
foreach (TimeLog::where([['board_id','>',0],['task_id', '>', 0]])->cursor() as $timelog) {
//do the other stuffs here
}
这看起来像第一个,但您已经将查询缩小到您需要的范围:
TimeLog::where([['board_id','>',0],['task_id', '>', 0]])->get()
Eager Loading已经提供了您需要的关系,但也可能会在内存中导致更多数据。因此,块方法可能会使事情更容易管理(即使您需要加载相关模型)
TimeLog::with(['board','task'], function ($query) {
$query->where([['board_id','>',0],['task_id', '>', 0]]);
}])->get();
您只需使用Transformer
即可虽然这可能不能完全解决问题,但是因为你面临的主要问题是基于内存管理,所以上面的方法应该是有用的。
- 第二个问题 -
基于Laravel API here您可以看到:
它只返回基础查询构建器实例。根据我的观察,根据你的例子不需要它。
<强>更新强>
对于问题1,因为看起来你只想简单地将结果作为响应返回,所以,对这个结果进行分页会更有效率。 Laravel offers pagination最简单的是SimplePaginate,这很好。唯一的问题是它会对数据库进行更多的查询,但是会检查最后一个索引;我猜它也使用cursor
但不确定。我想最后这可能更理想,有:
return TimeLog::paginate(1000);
答案 1 :(得分:0)
我遇到过类似的问题。这里的主要问题是,Elloquent执行大量任务的速度非常慢,因为它同时获取所有结果,因此简短的答案是使用PDO提取逐行获取。
简短的例子:
$db = DB::connection()->getPdo();
$query_sql = TimeLog::join('oc_boards', 'oc_boards.id', '=', 'oc_time_logs.board_id')
->join('oc_tasks', 'oc_tasks.id', '=', 'oc_time_logs.task_id')
->join('oc_users', 'oc_users.id', '=', 'oc_time_logs.user_id')
->select('oc_boards.title AS board_title', 'oc_tasks.title AS task_title','oc_time_logs.id','oc_time_logs.time_used_sec','oc_users.id AS user_id')
->toSql();
$query = $db->prepare($query->sql);
$query->execute();
$logs = array();
while ($log = $query->fetch()) {
$log_filled = new TimeLog();
//fill your model and push it into an array to parse it to json in future
array_push($logs,$log_filled);
}
return response()->json($logs);