Здравствуй, уважаемый друг!

Кажется, ты используешь AdBlock. Этот проект развивается и существует за счет доходов от рекламы.
Добавь, пожалуйста, нас в исключения.

Спасибо за понимание.

Создание сайтов Харьков

Поиск продуктов по атрибутам в WebAsyst ShopScript

16 февраля 2013 в 17:01 Автор: Nikita_Sp в категории Статьи о php 2 комментария

Добрый день, уважаемые читатели блога!

Давно не писал в блог в связи с отсутствием интересного и необычного. Но! Вчера мир перевернулся! И не потому что на Землю летят метеориты и т.п.

Дело в том, что сейчас мной дорабатывается движок компании WebAsyst ShopScript, а именно улучшается работа поиска по атрибутам.

По умолчанию, поиск по атрибутам предусматривает выбор одного значения каждого из атрибутов, а в улучшенной версии поиска можно выбирать несколько значений атрибута, с помощью checkbox’ов.

В чем же проблема у движка ShopScript?
Итак, открыв файл C:\WebServers\home\domain\www\published\SC\html\scripts\core_functions\product_functions.php
Я внес все необходимые изменения чтобы появилась возможность выбирать несколько атрибутов и все это нормально обрабатывалось.
После пары моих тестов на локальной машине все работало отлично, но страница достаточно долго грузилась, и я решил посмотреть логи БД MSQL, где увидел запросы следующего типа:


SELECT SQL_CALC_FOUND_ROWS p.*, name_ru AS _name_sort FROM SC_products p
 LEFT JOIN SC_product_options_values PrdOptVal0 ON p.productID=PrdOptVal0.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet0 ON p.productID=PrdOptSet0.`productID`

LEFT JOIN SC_product_options_values PrdOptVal1 ON p.productID=PrdOptVal1.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet1 ON p.productID=PrdOptSet1.`productID`

LEFT JOIN SC_product_options_values PrdOptVal2 ON p.productID=PrdOptVal2.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet2 ON p.productID=PrdOptSet2.`productID`

LEFT JOIN SC_product_options_values PrdOptVal3 ON p.productID=PrdOptVal3.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet3 ON p.productID=PrdOptSet3.`productID`

LEFT JOIN SC_product_options_values PrdOptVal4 ON p.productID=PrdOptVal4.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet4 ON p.productID=PrdOptSet4.`productID`

LEFT JOIN SC_product_options_values PrdOptVal5 ON p.productID=PrdOptVal5.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet5 ON p.productID=PrdOptSet5.`productID`

LEFT JOIN SC_product_options_values PrdOptVal6 ON p.productID=PrdOptVal6.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet6 ON p.productID=PrdOptSet6.`productID`

LEFT JOIN SC_product_options_values PrdOptVal7 ON p.productID=PrdOptVal7.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet7 ON p.productID=PrdOptSet7.`productID`

LEFT JOIN SC_product_options_values PrdOptVal8 ON p.productID=PrdOptVal8.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet8 ON p.productID=PrdOptSet8.`productID`

LEFT JOIN SC_product_options_values PrdOptVal9 ON p.productID=PrdOptVal9.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet9 ON p.productID=PrdOptSet9.`productID`

LEFT JOIN SC_product_options_values PrdOptVal10 ON p.productID=PrdOptVal10.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet10 ON p.productID=PrdOptSet10.`productID`

LEFT JOIN SC_product_options_values PrdOptVal11 ON p.productID=PrdOptVal11.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet11 ON p.productID=PrdOptSet11.`productID`

LEFT JOIN SC_product_options_values PrdOptVal12 ON p.productID=PrdOptVal12.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet12 ON p.productID=PrdOptSet12.`productID`

LEFT JOIN SC_product_options_values PrdOptVal13 ON p.productID=PrdOptVal13.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet13 ON p.productID=PrdOptSet13.`productID`

LEFT JOIN SC_product_options_values PrdOptVal14 ON p.productID=PrdOptVal14.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet14 ON p.productID=PrdOptSet14.`productID`

LEFT JOIN SC_product_options_values PrdOptVal15 ON p.productID=PrdOptVal15.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet15 ON p.productID=PrdOptSet15.`productID`

LEFT JOIN SC_product_options_values PrdOptVal16 ON p.productID=PrdOptVal16.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet16 ON p.productID=PrdOptSet16.`productID`

LEFT JOIN SC_product_options_values PrdOptVal17 ON p.productID=PrdOptVal17.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet17 ON p.productID=PrdOptSet17.`productID`

LEFT JOIN SC_product_options_values PrdOptVal18 ON p.productID=PrdOptVal18.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet18 ON p.productID=PrdOptSet18.`productID`

LEFT JOIN SC_product_options_values PrdOptVal19 ON p.productID=PrdOptVal19.`productID`
 LEFT JOIN SC_product_options_set PrdOptSet19 ON p.productID=PrdOptSet19.`productID`
 WHERE ((categoryID IN (558,559,560,561,562,563,564,565,566,567,568,569,590,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,591,592,593,594,595,557)) AND ( enabled=1)) AND (
 PrdOptVal0.optionID=17
 AND
 (
 ( PrdOptVal0.option_type=1 AND PrdOptSet0.variantID=18)
 OR
 (PrdOptVal0.option_type=0 AND PrdOptVal0.option_value_ru LIKE '%Смартфон%')
 )) OR (
 PrdOptVal0.optionID=17
 AND
 (
 ( PrdOptVal0.option_type=1 AND PrdOptSet0.variantID=17)
 OR
 (PrdOptVal0.option_type=0 AND PrdOptVal0.option_value_ru LIKE '%Телефон%')
 )) AND (
 PrdOptVal1.optionID=19
 AND
 (
 ( PrdOptVal1.option_type=1 AND PrdOptSet1.variantID=19)
 OR
 (PrdOptVal1.option_type=0 AND PrdOptVal1.option_value_ru LIKE '%Android 2.2%')
 )) OR (
 PrdOptVal1.optionID=19
 AND
 (
 ( PrdOptVal1.option_type=1 AND PrdOptSet1.variantID=21)
 OR
 (PrdOptVal1.option_type=0 AND PrdOptVal1.option_value_ru LIKE '%Android 2.3%')
 )) AND (
 PrdOptVal2.optionID=20
 AND
 (
 ( PrdOptVal2.option_type=1 AND PrdOptSet2.variantID=31)
 OR
 (PrdOptVal2.option_type=0 AND PrdOptVal2.option_value_ru LIKE '%1 слот%')
 )) AND (
 PrdOptVal3.optionID=23
 AND
 (
 ( PrdOptVal3.option_type=1 AND PrdOptSet3.variantID=71)
 OR
 (PrdOptVal3.option_type=0 AND PrdOptVal3.option_value_ru LIKE '%MTK%')
 )) AND (
 PrdOptVal4.optionID=24
 AND
 (
 ( PrdOptVal4.option_type=1 AND PrdOptSet4.variantID=103)
 OR
 (PrdOptVal4.option_type=0 AND PrdOptVal4.option_value_ru LIKE '%1.0 GHz%')
 )) AND (
 PrdOptVal5.optionID=25
 AND
 (
 ( PrdOptVal5.option_type=1 AND PrdOptSet5.variantID=83)
 OR
 (PrdOptVal5.option_type=0 AND PrdOptVal5.option_value_ru LIKE '%Mali 300%')
 )) AND (
 PrdOptVal6.optionID=26
 AND
 (
 ( PrdOptVal6.option_type=1 AND PrdOptSet6.variantID=87)
 OR
 (PrdOptVal6.option_type=0 AND PrdOptVal6.option_value_ru LIKE '%1 Гб%')
 )) AND (
 PrdOptVal7.optionID=27
 AND
 (
 ( PrdOptVal7.option_type=1 AND PrdOptSet7.variantID=60)
 OR
 (PrdOptVal7.option_type=0 AND PrdOptVal7.option_value_ru LIKE '%1 Гб%')
 )) AND (
 PrdOptVal8.optionID=28
 AND
 (
 ( PrdOptVal8.option_type=1 AND PrdOptSet8.variantID=65)
 OR
 (PrdOptVal8.option_type=0 AND PrdOptVal8.option_value_ru LIKE '%до 1 Гб%')
 )) AND (
 PrdOptVal9.optionID=29
 AND
 (
 ( PrdOptVal9.option_type=1 AND PrdOptSet9.variantID=100)
 OR
 (PrdOptVal9.option_type=0 AND PrdOptVal9.option_value_ru LIKE '%aGPS%')
 )) AND (
 PrdOptVal10.optionID=31
 AND
 (
 ( PrdOptVal10.option_type=1 AND PrdOptSet10.variantID=34)
 OR
 (PrdOptVal10.option_type=0 AND PrdOptVal10.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal11.optionID=32
 AND
 (
 ( PrdOptVal11.option_type=1 AND PrdOptSet11.variantID=116)
 OR
 (PrdOptVal11.option_type=0 AND PrdOptVal11.option_value_ru LIKE '%ёмкостной%')
 )) AND (
 PrdOptVal12.optionID=36
 AND
 (
 ( PrdOptVal12.option_type=1 AND PrdOptSet12.variantID=91)
 OR
 (PrdOptVal12.option_type=0 AND PrdOptVal12.option_value_ru LIKE '%0,3 Мп%')
 )) AND (
 PrdOptVal13.optionID=38
 AND
 (
 ( PrdOptVal13.option_type=1 AND PrdOptSet13.variantID=39)
 OR
 (PrdOptVal13.option_type=0 AND PrdOptVal13.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal14.optionID=39
 AND
 (
 ( PrdOptVal14.option_type=1 AND PrdOptSet14.variantID=16)
 OR
 (PrdOptVal14.option_type=0 AND PrdOptVal14.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal15.optionID=40
 AND
 (
 ( PrdOptVal15.option_type=1 AND PrdOptSet15.variantID=36)
 OR
 (PrdOptVal15.option_type=0 AND PrdOptVal15.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal16.optionID=41
 AND
 (
 ( PrdOptVal16.option_type=1 AND PrdOptSet16.variantID=11)
 OR
 (PrdOptVal16.option_type=0 AND PrdOptVal16.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal17.optionID=42
 AND
 (
 ( PrdOptVal17.option_type=1 AND PrdOptSet17.variantID=29)
 OR
 (PrdOptVal17.option_type=0 AND PrdOptVal17.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal18.optionID=43
 AND
 (
 ( PrdOptVal18.option_type=1 AND PrdOptSet18.variantID=13)
 OR
 (PrdOptVal18.option_type=0 AND PrdOptVal18.option_value_ru LIKE '%есть%')
 )) AND (
 PrdOptVal19.optionID=15
 AND
 (
 ( PrdOptVal19.option_type=1 AND PrdOptSet19.variantID=5)
 OR
 (PrdOptVal19.option_type=0 AND PrdOptVal19.option_value_ru LIKE '%белый%')
 )) GROUP BY p.productID ORDER BY sort_order, in_stock DESC, _name_sort LIMIT 0,15

38 LEFT JOIN’oв! (для понимания — 2 джоина = 0,02 сек, 4 джоина = 8,8 сек.)
По два на каждый атрибут! Т.е. если у вас у товара хотя бы 5 атрибутов — готовьтесь к 10 JOIN’aм, которые положат вашу БД! 😉

Я обратил внимание на 543 и 512 строчки вышеупомянутого файла. Там расположено тело функции _prepareSearchExtraParameters, которая собственно и отвечает за поиск продуктов.

Что же такого в этих строчках? Объясню.
Не знаю, для чего нужен был этот костыль, и возможно я не прав, за что заранее приношу свои извенения разработчикам, но в теле цикла, начинающего на 507 строке начинает генерироваться для каждого атрибута 2хJOIN LEFT, и при всем при этом присоединяется одна и та же таблица просто с каждым разом ее имя PrdOptVal инкрементируется (PrdOptVal1, PrdOptVal2). Как мы все знаем, JOIN’ы не самые легкие запросы, и для чего присоединять одну и ту же таблицу много раз?

Таким образом я пришел к выводу, что не все то хороший движок, что стоит 7950 руб!
Вот так, дорогие друзья! Удачного кодинга и оптимизированного кода вам! 😉

Продолжение тут — WebAsyst наплевать на владельцев ShopScript или как положить БД владельца интернет-магазина ShopScript

2 комментария

  • join’ов много для комбинирования фиксированных строковых параметров и выбираемых, если параметров не больше 1 на каждую опцию то запрос довольно быстро проходит, а если же параметров больше то время запроса растёт по экспоненциальной. По идее там надо 4 join’а 2 на фиксированные и 2 на выбираемые параметры, я сделал так — убрал перекрёстные сравнения строковых параметров с выбираемыми и использовал straight join плюс отсортировал так чтобы сначала джоинились бы опции у которых меньше параметров. Ну и плюс ограничение на 61 join — итого доступно всего 30 параметров для выбора 🙂 А убрать $cnt у меня не получилось просто ничего не находил 🙂 Щас запрос размером 50кб идёт за 2 сек — многовато но там свои нюансы 🙂

    • Дело в том что присоединяются одни и те же таблицы с одними и теми же параметрами. Смысла 0. Я думал что это некий необходимый костыль, но так и не нашел ему применения. Если вы можете предложить вариант где оправдывается такое поведение запроса — благодарность )

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вверх!

Меню блога

Категории блога

Облако тегов