За последние 24 часа нас посетили 16578 программистов и 1650 роботов. Сейчас ищет 1961 программист ...

Обновление одной таблицы с учетом другой

Тема в разделе "MySQL", создана пользователем Deonis, 19 авг 2016.

  1. Deonis

    Deonis Старожил

    С нами с:
    15 фев 2013
    Сообщения:
    1.521
    Симпатии:
    504
    Задача, в общем-то, не новая, но проблема возникла в том, что таблицы достаточно массивные для нужной операции (~80к). А суть состоит в том, что в таблице "A" нужно установить определенному полю значение "0", если в таблице "B" нет записи с таким же ID и штрих-кодом. Пробовал запросы и с помощью LEFT OUTER JOIN, и NOT EXISTS, но результат один - через пять минут финал по таймауту. Увеличивать max_execution_time не выход, т.к. и пять минут как-то многовато, тем более, что операция будет проводится ежедневно. Вот хочу посоветоваться, какие могут быть альтернативные варианты.
    На всякий случай, покажу запрос, который нормально работал, по в таблицах было масло записей (до 1к):
    PHP:
    1. <?php
    2. $query = "UPDATE
    3.    `A`
    4. LEFT OUTER JOIN `B` ON `A`.`barcode` = `B`.`barcode`
    5.    AND `A`.`id` = `B`.`id`
    6. SET
    7.    `A`.`field` = 0
    8. WHERE
    9.    `B`.`barcode` IS NULL";
     
  2. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Выборка из источника по условию быстро проходит? Отключение индексов на время обновления целевой таблицы допустимо?
     
  3. Deonis

    Deonis Старожил

    С нами с:
    15 фев 2013
    Сообщения:
    1.521
    Симпатии:
    504
    @Ganzal я бы не сказал, что очень быстро. К примеру, поиск по тому же штрих-коду и id, занимает около 0.0490 сек. Отключить индексы - допустимо.

    P.S. Наверно нужно добавить, что вторая таблица существует только для импорта данных. В неё заливаются данные из XML-файла, а потом проводится ряд обновлений в рабочих таблицах. Все эти операции (их всего 7), выполняются за секунд 20, а вот эта последняя всю картину портит.
     
  4. Deonis

    Deonis Старожил

    С нами с:
    15 фев 2013
    Сообщения:
    1.521
    Симпатии:
    504
    @Ganzal , сейчас попробовал с отключенными индексами, но результат, к сожалению, отрицательный.
     
  5. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Ты на пхп обрабатываешь это переливание? Может попробовать "пакетами" накостылить?
     
  6. Deonis

    Deonis Старожил

    С нами с:
    15 фев 2013
    Сообщения:
    1.521
    Симпатии:
    504
    Да, на PHP. Пакетами - это в каком смысле? Разбить XML-файл на несколько небольших и пройтись по ним поочерёдно или частями обновлять в мускуле?
     
  7. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Ну что-то типа. Ну или выбрать все пары баркод+первичный ключ из главной таблицы, разместить их в массиве, выбрать баркоды из второй таблицы, поудалять ключи с этими баркодами из массива, и потом сделать один или несколько запросов вида "update A set field = 0 where id in (.....)". По идее для первичного ключа индекс будет быстро работать.

    А ты explain-ить запрос не пробовал? Мускул не находит в нем проблем? Может там где индекс какой пропадает. Или даже не создан.
     
    Deonis нравится это.
  8. Deonis

    Deonis Старожил

    С нами с:
    15 фев 2013
    Сообщения:
    1.521
    Симпатии:
    504
    Мускул 5.5. Там EXPLAIN UPDATE вроде бы нету :( Сейчас буду пробовать порциями заливать.
     
  9. Deonis

    Deonis Старожил

    С нами с:
    15 фев 2013
    Сообщения:
    1.521
    Симпатии:
    504
    В общем, способом научного тыка, определил достаточно оптимальный способ, на который так же натолкнул @Ganzal. Выбрал из временной таблицы баркод и id, сконкатенировав их в самом запросе через дефис, полученный массив флипнул (isset работает быстрее, чем in_array) и выбрал баркод и id по отдельности из основной таблицы:
    PHP:
    1. <?php
    2. // массив для отсутвующих ID-шников
    3. $missing_id = [];
    4.  
    5. // Полученный в итоге массив из временной таблицы, типа такого
    6. $tmp_keys = [
    7.     '123456789-555' => 1,
    8.     '987654321-123' => 2,
    9.     /* ... */
    10. ];
    11. // Выбрали баркод и ID из целевой таблицы, проверяем в цикле
    12. while ($line = $sth->fetch(\PDO::FETCH_ASSOC)) {
    13.     if ( !isset($tmp_keys[$line['barcode'] . '-' . $line['id']]) ) {
    14.         $missing_id[] = $line['id'];
    15.     }
    16. }
    17. // Дальше можно проверить размер $missing_id,
    18. // если большой, то разбить на части array_chunk и обновляем записи в БД
    Результат более чем приемлемый. Именно эта операция, которая ранее занимала 5+ минут, сейчас в среднем выполняется за 0.305 сек! Не буду грешить на MySQL, т.к. тормоза с предыдущим вариантом пока для меня остались загадкой, но пхп справился отлично.
     
    #9 Deonis, 20 авг 2016
    Последнее редактирование: 20 авг 2016