Билдер SQL запросов для modx, minishop2 и TV
Всем привет, просили меня недавно сделать выгрузку со старого сайта на modx с более чем 200к товаров, т.к. я уже давно не работал с modx, абсолютно забыв методы API, а также помня, насколько медленно и ресурсозатратно оно работает было решено писать SQL напрямую и все бы ничего, пока я не увидел структуру tv полей. Руками писать SQL с выборкой необходимых TV было адским адом, и было решено за часик накидать мини билдер SQL запросов
Получился примерно такой в использовании билдер, по мне покрывает 100% моих задач в формировании SQL для выгрузок из modx & ms2:
Если хочется сразу к коду
И так, к SQL который сформировал код выше:
не удивительно соб-но =\.
Если колонка WHERE присутствует хоть в одном из SELECT, то ему автоматически подставится префикс, а значит отсутствие конфликтов, и можно указывать прямо так:
Получился примерно такой в использовании билдер, по мне покрывает 100% моих задач в формировании SQL для выгрузок из modx & ms2:
$builder = new SimpleBuilder();
$builder->addResourceFields('id', 'pagetitle', 'longtitle')
->addMs2Fields('article', 'price')
->addTvFields('price_opt', 'product_time', 'valute', 'remains', 'product_tax')
->where('deleted', '=', false)
->where('published', '=', true)
->where('class_key', '=', 'msProduct')
->where('remains', '!=', 0)
->whereNotNull('remains')
->whereIn('id', [18559, 18560])
->limit(1)
->offset(1)
;
$sql = $builder->sql();
$products = $modx->query($sql)->fetchAll(PDO::FETCH_ASSOC);
Под катом код и результат SQL который формирует билдерЕсли хочется сразу к коду
И так, к SQL который сформировал код выше:
SELECT resource.id,
resource.pagetitle,
resource.longtitle,
ms2.article,
ms2.price,
price_opt_table.value as price_opt,
product_time_table.value as product_time,
valute_table.value as valute,
remains_table.value as remains,
product_tax_table.value as product_tax
FROM modx_site_content as resource
LEFT JOIN modx_ms2_products as ms2 on resource.id = ms2.id
LEFT JOIN modx_site_tmplvars as price_opt_tv_name on price_opt_tv_name.name = 'price_opt'
LEFT JOIN modx_site_tmplvar_contentvalues as price_opt_table
on resource.id = price_opt_table.contentid and price_opt_table.tmplvarid = price_opt_tv_name.id
LEFT JOIN modx_site_tmplvars as product_time_tv_name on product_time_tv_name.name = 'product_time'
LEFT JOIN modx_site_tmplvar_contentvalues as product_time_table
on resource.id = product_time_table.contentid and
product_time_table.tmplvarid = product_time_tv_name.id
LEFT JOIN modx_site_tmplvars as valute_tv_name on valute_tv_name.name = 'valute'
LEFT JOIN modx_site_tmplvar_contentvalues as valute_table
on resource.id = valute_table.contentid and valute_table.tmplvarid = valute_tv_name.id
LEFT JOIN modx_site_tmplvars as remains_tv_name on remains_tv_name.name = 'remains'
LEFT JOIN modx_site_tmplvar_contentvalues as remains_table
on resource.id = remains_table.contentid and remains_table.tmplvarid = remains_tv_name.id
LEFT JOIN modx_site_tmplvars as product_tax_tv_name on product_tax_tv_name.name = 'product_tax'
LEFT JOIN modx_site_tmplvar_contentvalues as product_tax_table
on resource.id = product_tax_table.contentid and product_tax_table.tmplvarid = product_tax_tv_name.id
WHERE deleted = 0
AND published = 1
AND class_key = 'msProduct'
AND remains_table.value != 0
AND remains_table.value IS NOT NULL
AND resource.id IN (18559, 18560)
LIMIT 1 OFFSET 1
Из приятного:
Прирост скорости по сравнению с $modx->getCollection() более чем в 10 раз (!)Если колонка WHERE присутствует хоть в одном из SELECT, то ему автоматически подставится префикс, а значит отсутствие конфликтов, и можно указывать прямо так:
$builder->where('id', '=', 1);
т.е. без указания таблицы, к которой принадлежит эта колонкаИз неприятного:
- Нет выборки картинок ms2_product_files, для моей задачи этого не надо было, по этому и не писал
- Он написан за час, а эта заметка за 10 минут, не исключено наличие ошибок, а качество кода оставляет желать лучшего
- Если колонка ресурса будет пересекаться по названию например с колонкой TV вы наверняка получите ошибку, будьте внимательны
- Дефолтное значение tv параметра не подтягивается, подставляйте его в коде при выгрузке, а лучше вообще не используйте
Код смотреть тут
Требования: php >= 7.1
Поблагодарить автора
Отправить деньги
Комментарии: 11
Прирост скорости по сравнению с $modx->getCollection() более чем в 10 разА почему сравнивается с getCollection, а не с newQuery?
Я честно говоря уже настолько долго не работал с modx что понятия не имею что за newQuery, если есть возможность и время, я был бы рад увидеть пример подобного запроса на newQuery, если там надо руками джойнить таблицы с тв, то какой в нем смысл по сравнению с написанием обычного SQL?
Главная причина почему пришлось писать билдер, это адская структура таблиц тв полей и сложность джоина каждого из параметров
Главная причина почему пришлось писать билдер, это адская структура таблиц тв полей и сложность джоина каждого из параметров
Я задавал вопрос именно в контексте разницы скорости, а не удобства, ибо думается мне, что разница в скорости с твоим решением либо будет минимальна, либо вообще её не будет.
Очевидно что не будет, а еще она также будет минимальна если все эти лефт джоины прописать руками в mysql, только вот newQuery так себе билдер
И код на newQuery будет скорее всего похож по количеству и структуре на выходной SQL
И код на newQuery будет скорее всего похож по количеству и структуре на выходной SQL
почему сравнивается getCollection, а не с newQueryПотому что в случае с getCollection не надо джойнить tv, а можно их получить через getTVValue через модель собственно
Я ж и попросил показать код на newQuery, мне не очень понятно зачем я должен его использовать, если я могу написать то же самое по объему и сложности на обычном SQL
Я не говорю, что ты должен его использовать. Я просто заметил, что ты сравниваешь getCollection, который предназначен не совсем для подобных дел, и свой быстрый билдер запросов, когда стоило последний сравнивать с более подходящим конкурентом – newQuery.
$q = $modx->newQuery('msProduct')
->leftJoin('modTemplateVar', 'price_opt_tv_name', 'price_opt_tv_name.name = "price_opt"')
->leftJoin('modTemplateVarResource', 'price_opt_table', 'price_opt_table.tmplvarid = price_opt_tv_name.id AND price_opt_table.contentid = msProduct.id')
->where([])
...
примерно так будет. примерно так будет.Ну вот об этом я и говорю, какой смысл использовать тогда newQuery если все то же самое и в таком же количестве можно написать напрямую в SQL?
getCollection, который предназначен не совсем для подобных делА для каких дел он тогда предназначен, если не может построить оптимальный запрос?
когда стоило последний сравнивать с более подходящим конкурентом – newQuery.Не знаю, я все таки считаю что сравнивать с newQuery не корректно, newQuery просто транслирует php команды в SQL код, он не автоматизирует ничего и не упрощает
А 200к товаров хранятся в отдельной таблице или в modx_site_content?
Все стандартно, кроме прикрученного эластика для поиска все нормально справляется если к этому вопрос )
Ага, спасибо. А эластик используется для фасетного поиска?
Нет, обычный полнотекстовой поиск, там не такие товары и не такой заказчик чтобы заморачиваться с фильтрами и фасетным поиском) Наверное первый, кто дойдет до задачи фасетного поиска на этом проекте, просто перетащит его на какой нибудь условный ларавель, чтобы не иметь в будущем проблем)))
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.