Можете ли вы оптимизировать этот код запроса цикла PHP? - PullRequest
0 голосов
/ 23 июня 2019

У меня проблемы с производительностью на моем веб-сайте 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 ]

1 голос
/ 23 июня 2019

Вы можете свести весь алгоритм к одному 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)
1 голос
/ 23 июня 2019

Есть несколько вещей, которые вы можете сделать, чтобы ускорить это, повысить производительность и уменьшить количество обращений к базе данных / вызову функции.

Во-первых, вы можете вытащить все свои ToolTransactionInfo сразу, прежде чем начать цикл. Это поможет вам сделать только одно извлечение БД. Итак, над вашей петлей:

$transactionInfosTotal = ToolTransactionInfo::get();

Это дает вам всю необходимую информацию. Затем, внутри вашего цикла, вы можете выполнить тот же запрос, но сделать это для коллекции, уже находящейся в памяти (т.е. не возвращаться в БД).

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

Таким образом, вы получаете ту же коллекцию внутри своих циклов для нужного дня и инструмента - и затем вы можете выполнять свои вычисления или перемещаться в массив по своему усмотрению. Это просто сохраняет попадания в БД / делает его более эффективным и быстрым.

Другая вещь, которую вы можете сделать, чтобы немного ускориться, это вызвать время (Carbon) один раз. Не огромная экономия, но, вероятно, будет немного быстрее. Перед вашими циклами установите «сейчас» в переменную:

$now = Carbon(now);

Затем используйте это значение, хранящееся в переменной внутри ваших циклов, для обработки ваших временных вычислений, поскольку теперь не нужно возвращаться к методу, чтобы получить его сейчас, оно уже хранится в памяти:

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

НТН

0 голосов
/ 23 июня 2019

Запрос Slepic был путь. Он увеличил общую загрузку страницы с 3,8 до 2,5 секунд (объем данных страницы, но все же некоторые улучшения), но это была одна из двух самых тяжелых функций, которые я мог улучшить. Спасибо, 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;
...