laravel будет ли лагать такой запрос, и как это исправить

Есть такие таблицы users, eggs, birds, sellers, certificates Каждый час или даже лучше каждые 10 минут мне нужно изменять и добавлять записи в таблицу eggs.

Для этого я сделал scheduler, запись cron и command, в которой такой запрос

public function handle() {
  // Метод возвращает массив  данных из таблиц birds и seller->certificates
  $birds_users = User::get_all_users_birds_with_certificate();

  // перебираем все это
  foreach($birds_users as $key => $birds_user) {
    // получаем пользователя по key (key - это user id)
    $user = User::with('my_eggs') -> find($key);

    // теперь перебираем каждую птицу пользователя по отдельности
    foreach($birds_user[0] as $bird) {
      // проверяем есть ли уже такая запись
      if (
        $user -> my_eggs -> contains('user_id', '===', $key) &&
        $user -> my_eggs -> contains('bird_seller_id', '===', $bird["bird_seller_id"])
      ) {
        // берем эту запись
        $birdRow = Egg::where('user_id', $key) - > where('bird_seller_id', $bird["bird_seller_id"]) - > first();

        // и обновляем нужные поля
        $birdRow - > demand = $bird["demand"]; // if the characteristics of the bird will change
        $birdRow - > count += $bird["count"] * $bird["fertility"]; // increase eggs
        $birdRow - > update();
      } else {
        // такой записи еще нет, нужно ее создать
        Egg::create([
          'user_id' => $key,
          'bird_seller_id' => $bird["bird_seller_id"],
          'price' => $bird["egg_price"],
          'demand' => $bird["demand"],
          'count' => $bird["count"] * $bird["fertility"],
        ]);
      }
    }
  }
}

ТО есть получается, что каждый час (или 10 минут) получаем каждого пользователя по отдельности, для каждой его птицы так же выполняем запрос. Это может вызывать большую нагрузку? Если да, то как это можно исправить


Ответы (2 шт):

Автор решения: Majestio

Я думаю так делать не следует. Гонять данные между веб-сервером и сервером базы данных для массовых обновлений - не лучшее решение. Нужно писать сырой запрос и отсылать его серверу базы данных, пусть он там у себя все быстренько сделает.

В качестве отправной точки для написания запроса:

  1. MySQL - UPDATE query based on SELECT Query
  2. Insert into a MySQL table or update if exists
→ Ссылка
Автор решения: Color kat

Да, посылать такие запросы через laravel - не лучшая идея. Нужно написать один сырой sql запрос. Сначала было страшно приступать к этому, так как в sql я писал только SELECT, иногда UPDATE. А тут куча связей. Но потом оказалось, что это не так и страшно, даже интересно)

INSERT INTO eggs (id, name, birds_count, price, demand, count, litter, collected, user_id, bird_seller_id)

SELECT 
    CONCAT(u.id, b_s.id), -- create UID from user_id and bird_seller_id
    birds.name, -- bird name
    count, -- count of birds from bird_seller_user table
    birds.egg_price * (1 + IFNULL(price_bonus, 0) / 100), -- egg price with certificate
    birds.demand * (1 + IFNULL(demand_bonus, 0) / 100), -- demand with certificate bonus
    count * birds.fertility * (1 + IFNULL(fertility_bonus, 0) / 100), -- get count of eggs from birds_count * fertility with certificate bonus
    count * birds.litter * (1 + IFNULL(litter_bonus, 0) / 100),
    0, -- change collected
    user_id,
    bird_seller_id 
    
FROM users AS u -- get all users

LEFT JOIN bird_seller_user AS b_s_u ON (b_s_u.user_id = u.id) -- join bird_seller_user table to get sold_bird

JOIN bird_seller AS b_s ON (b_s.id = b_s_u.bird_seller_id) -- join bird_seller table (this id sol_bird)

JOIN birds ON (b_s.bird_id = birds.id) -- join birds table

LEFT JOIN certificates AS certs ON ((SELECT certificate_id FROM sellers WHERE id = b_s.seller_id) = certs.id) -- and join certificate table

ON DUPLICATE KEY UPDATE 
    name = birds.name, -- update bird name (just in case)
    birds_count = b_s_u.count,
    price = birds.egg_price * (1 + IFNULL(price_bonus, 0) / 100),
    demand = birds.demand,
    count = eggs.count + b_s_u.count * birds.fertility * (1 + IFNULL(fertility_bonus, 0) / 100), -- increase eggs count
    litter = b_s_u.count * birds.litter * (1 + IFNULL(litter_bonus, 0) / 100),
    collected = 0
    

через join связываем все таблицы, аналог belongsTo в ларе, Вставляем эти данные через INSERT INTO. А через ON DUPLICATE KEY UPDATE обновляем запись, если она уже есть (Он работает, если совпадает id)

→ Ссылка