您可以优化此PHP循环查询代码吗?

时间:2019-06-23 00:59:23

标签: php mysql laravel optimization query-optimization

我的Laravel网站出现一些性能问题。 我发现原因是我的代码的一部分。 我在一个循环中进行查询,因为我刚刚发现它不应该这样做。 我不确定如何才能以最佳方式优化此代码(无需更改数据库,因为在此阶段这还不那么容易)。

我当时正在考虑使用foreach $transactionInfos来改善这种部分,但是现在我不确定Laravel的语法:

$transactionInfos = ToolTransactionInfo::where('tool_id', $tool->id)
                    ->where('date', $date)
                    ->only(['users'])
                    //->get();
                    ->all();

if(!empty($transactionInfos)) {
                    $users = array_sum($transactionInfos);
                }
                else{
                    $users = 0;
                }

实际代码:

        foreach ($data['tools'] as $tool) {
            // 30 day user graph data
            $count = 30;
            $tool_users_30d[$tool->id] = [];
            while ($count > 0) {
                $date = Carbon::now()->subDays($count)->format('Y-m-d');

                $transactionInfos = ToolTransactionInfo::where('tool_id', $tool->id)
                    ->where('date', $date)
                    ->get();

                $users = 0;
                foreach ($transactionInfos as $transactionInfo) {
                    $users += $transactionInfo->users;
                } 

                array_push($tool_users_30d[$tool->id], $users);
                $count--;
            }
        }
        $data['tool_users_30d'] = $tool_users_30d;

我希望得到一个包含所有工具列表的数组,其中包含每个30天用户数据的数组,例如:

  • [13] [0] = 20
  • [13] [1] = 6
  • [13] [2] = 24
  • ..
  • [13] [29] = 10

  • ..

  • [18] [0] = 50

  • [18] [1] = 11
  • [18] [2] = 55
  • ..
  • [18] [29] = 6

https://i.imgur.com/VDyZ9uN.png (示例图像显示0个用户,因为本地数据库为空,但这看起来应该是这样)。

3 个答案:

答案 0 :(得分:1)

您可以采取以下几项措施来加快速度,以提高性能,并减少对数据库的访问/对函数的调用。

首先,您可以在循环之前一次拉出所有ToolTransactionInfo。这将帮助您仅执行一次数据库拉取。因此,在循环上方:

$transactionInfosTotal = ToolTransactionInfo::get();

这为您提供了所需的所有信息。然后,在循环中,您可以执行相同的查询,但对内存中已存在的集合执行此查询(即,不返回数据库)。

 $transactionInfos = $transactionInfosTotal->where('tool_id', $tool->id)  
                ->where('date', $date)
                ->get();

有了这个,您就可以在循环中获得相同的集合,以适合正确的日子和工具-然后可以执行计算或根据需要移动到数组。这样只会节省数据库命中数/使其更高效,更快。

您可以做的另一件事是加快时间(碳)的调用时间。节省不了多少,但可能会更快一些。在循环之前,将“ now”设置为变量:

$now = Carbon(now);

然后使用循环中存储在变量中的值进行时间计算,因为它不必返回到现在获取的方法,它已经在内存中了

$date = $now->subDays($count)->format('Y-m-d');

HTH

答案 1 :(得分:1)

您可以将整个算法简化为一个sql查询,类似这样(运行它,至少需要一些引号...),您只需要对结果进行一次遍历即可构建目标多维数组

SELECT tool_id, 30 - datediff(now(), date) as "offset", sum(users)
FROM transaction_info
WHERE date >= (date(now()) - interval 30 days) AND date < date(now())
GROUP BY tool_id, 30 - datediff(now(), date)

答案 2 :(得分:0)

slepic的查询是必经之路。它将页面总加载时间从3.8秒提高到2.5秒(数据页面繁重,但仍有待改进),但这是我可以改善的2个最重的功能之一。谢谢你slepic和所有其他建议。

这是我的解决方法:

        // 30 day user graph data
        // Prepare array list
        $loaded_tool_ids = [];
        foreach ($data['tools'] as $tool) {
            array_push($loaded_tool_ids, $tool->id);
        }
        $count = 30;
        $now = Carbon::now()->format('Y-m-d');
        $oldest_date = Carbon::now()->subDays($count)->format('Y-m-d');
        // Query tool history data
        $transactionInfos = ToolTransactionInfo::select('tool_id','date',DB::raw("SUM(users) as users"))
            ->whereIn('tool_id', $loaded_tool_ids)
            ->where('date', '>=', $oldest_date)
            ->where('date', '<', $now)
            ->groupBy('tool_id')
            ->groupBy('date')
            ->orderBy('tool_id', 'asc')
            ->orderBy('date', 'asc')
            ->get()
            ->toArray();

        // Fill output array with query data
        foreach($transactionInfos as $transactionInfo) {
            // Get date number
            $date_number = $count - Carbon::parse($transactionInfo['date'])->diffInDays($now);
            // Fill array
            if(!empty($tool_users_30d[$transactionInfo['tool_id']])){
                // Fill previous dates with 0 data
                $tool_users_30d[$transactionInfo['tool_id']][$date_number] = (int) $transactionInfo['users'];
            }
            else{
                // Create array with the tool id
                $tool_users_30d[$transactionInfo['tool_id']] = [];
                // Fill date
                $tool_users_30d[$transactionInfo['tool_id']][$date_number] = (int) $transactionInfo['users'];
            }
        }

        // Fill output array with loaded tools that have empty days
        foreach($loaded_tool_ids as $loaded_tool_id){
            if(empty($tool_users_30d[$loaded_tool_id])){
                // Create array with the tool id
                $tool_users_30d[$loaded_tool_id] = [];
            }
        }

        // Fill days with 0 data
        foreach($tool_users_30d as $key => $tool_users){
            while($count > 0){
                if(empty($tool_users[$count-1])){
                    $tool_users_30d[$key][$count-1] = 0;
                }
                $count--;
            }
            $count = 30;
            ksort($tool_users_30d[$key]);
        }
        $data['tool_users_30d'] = $tool_users_30d;