#php #mysql
Имею две таблицы с товарами интернет магазина. В одну попадают только товары от различных поставщиков с указанием ID поставщика. Во вторую попадают все цены и наличие по каждому товару первой таблице, по каждому складу поставщика. Так как SQL недавно появился в моей жизни в таких масштабах, приходится просить помощи, не во всем сам могу разобраться. Кратко о данных в таблицах: Таблица с товарами выглядит вот так Таблица с ценами так Как видно на изображениях, на один товар может приходиться несколько различных цен на различных складах. Написал процедуру для усреднения цен по складам каждого поставщика. CREATE PROCEDURE `average`(IN `v_id` INT, IN `v_in_id` INT, IN `v_sup_id` INT) BEGIN DECLARE vPrice INT; DECLARE vAmount INT; SET vPrice = (SELECT (SUM(buy_price*amount)/SUM(amount)) FROM td_prices WHERE buy_price > 0 AND amount > 0 AND item_id = v_in_id and sup_id = v_sup_id); SET vAmount = (SELECT SUM(amount) FROM td_prices WHERE item_id = v_in_id and sup_id = v_sup_id); UPDATE td_items SET buy_price = vPrice, amount = vAmount WHERE id = v_id; END;; Не могу сказать что хорошо написал, опыта не хватает, возможно и заглючит при нулевой цене или количестве, но пока работает. Процедуру выполняю в PHP скрипте - делаю выборку всех id,in_id,sup_id из таблицы items и подставляю их в мою процедуру, вызывая ее в цикле перебора этой самой выборки. Грубо говоря $data = $sql->get('SELECT * FROM td_items'); foreach($data as $item) { $sql->query('call avarage(_ids_from_$item_here)'); } Да, идея банальная и не самая производительная.. После выполнения процедуры таблица заполняется вполне корректно Собственно, вопрос: Сейчас на 10 тысяч имеющихся в таблице items записей выполнение всего моего скрипта занимает аж 10 минут (на ноутбуке). Это никуда не годится, я прекрасно понимаю что если объеденить foreach PHP и SQL в одной единой процедуре, процесс пойдет намного быстрее. Но опыта не хватает. Подскажите, пожалуйста, методы оптимизации всей этой "выборки". Второй день бьюсь с sql курсорами и переменными, результата и понимания 0
Ответы
Ответ 1
Использовать курсоры и любые циклы при работе с SQL надо в очень редких, особо тяжелых, случаях. Практически любую работу в SQL можно выполнить одним запросом. Ваш цикл по td_prices и вызов процедуры для обновления каждой отдельной строки можно заменить таким запросом: UPDATE td_items I JOIN (SELECT item_id, sup_id, SUM(IF(buy_price > 0 AND amount > 0,buy_price*amount,0)) /SUM(IF(buy_price > 0 AND amount > 0,amount,0)) as buy_price, SUM(amount) as amount FROM td_prices GROUP BY item_id, sup_id ) P ON I.in_id=P.item_id AND I.sup_id=P.sup_id SET I.buy_price=P.buy_price, I.amount=P.amount IF пришлось применить т.к. у вас почему то используются немного разные выборки для количества (берущие вообще все записи) и цен (берущие большие нуля записи). Вообще стоит подумать о дополнительных признаках по которым решать какие записи надо обновлять, а какие нет. Например вести поле timestamp в прайсах и менять только записи для которых в БД изменились какие либо цены с момента последнего обновления. Или вообще менять средние цены автоматом в триггере на изменение таблицы с прайсами.Ответ 2
Подход, при котором ваш PHP-скрипт проходится по таблице и обновляет поле у каждой записи, конечно, очень медленный. 10 минут еще не так много. Когда ваша таблица станет больше, ждать придётся гораздо дольше. А действительно ли вам нужно хранить поле со средней ценой? Если вы хотите получить список товаров со средними ценами можно использовать запрос: select g.id, /* остальные необходимые поля*/, SUM(p.buy_price*p.amount)/SUM(p.amount), SUM(p.amount) from td_goods g join td_prices p on p.item_id = g.id group by g.id, /* остальные необходимые поля*/ Можно на основе запроса сделать view и будет представление исходной таблицы, но с дополнительным полем содержащим усреднённую цену.
Комментариев нет:
Отправить комментарий