При загрузке данных из внешних источников (например, таблиц Excel) нередко возникает задача сопоставления упаковок и единиц измерения с уже существующими объектами в базе. Типичный сценарий: необходимо определить позиции номенклатуры, для которых в справочнике ЕдиницыИзмерения создано более одной записи (например, базовая единица «шт», а также дополнительные — «коробка» или «паллета»), и проставить соответствующие признаки в результирующей выборке.
Разберем, почему ручная построчная обработка через НайтиСтроки уступает запросам, какие ошибки часто допускаются при соединении подчиненных справочников и как правильно реализовать выборку с помощью языка запросов 1С.
Проанализируем исходную структуру запроса и выделим моменты, снижающие производительность и точность работы алгоритма:
ЕдиницыИзмерения.Владелец.Артикул = ДанныеИзТЗ.АртикулТЗ порождает неявное левое соединение со справочником Номенклатура. В таком виде СУБД не может задействовать штатный индекс подчиненного справочника (Владелец, Ссылка), что приводит к медленному сканированию таблиц. Правильный путь — сопоставить артикул напрямую со справочником Номенклатура, получить ссылку, и уже по ней связывать подчиненные единицы.НЕ ДанныеИзТЗ.АртикулТЗ ЕСТЬ NULL: При левом соединении таблица ДанныеИзТЗ является ведущей. Если в самой исходной таблице значений нет пустых значений, данная фильтрация не имеет практического смысла.Рассмотрим классический способ решения задачи средствами языка запросов. Если нам необходимо выделить только ту номенклатуру, у которой зарегистрировано более одной единицы измерения, применим группировку по владельцу с агрегатной функцией КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) и условием ИМЕЮЩИЕ.
Разберем по шагам создание такого запроса:
Номенклатура по артикулу.ЕдиницыИзмерения.Посмотрим на пример реализации данного запроса:
ВЫБРАТЬ
ТЗ_Excel.ДлинаШт КАК ДлинаШт,
ТЗ_Excel.ДлинаКор КАК ДлинаКор,
ТЗ_Excel.ДлинаПал КАК ДлинаПал,
ВЫРАЗИТЬ(ТЗ_Excel.Артикул КАК СТРОКА(25)) КАК АртикулТЗ
ПОМЕСТИТЬ ДанныеИзТЗ
ИЗ
&ТЗ_Excel КАК ТЗ_Excel
ИНДЕКСИРОВАТЬ ПО
АртикулТЗ
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДанныеИзТЗ.АртикулТЗ КАК АртикулТЗ,
Номенклатура.Ссылка КАК НоменклатураСсылка,
ДанныеИзТЗ.ДлинаШт КАК ДлинаШт,
ДанныеИзТЗ.ДлинаКор КАК ДлинаКор,
ДанныеИзТЗ.ДлинаПал КАК ДлинаПал
ПОМЕСТИТЬ ДанныеСНоменклатурой
ИЗ
ДанныеИзТЗ КАК ДанныеИзТЗ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
ПО (ДанныеИзТЗ.АртикулТЗ = Номенклатура.Артикул)
И (НЕ Номенклатура.ЭтоГруппа)
И (НЕ Номенклатура.ПометкаУдаления)
ИНДЕКСИРОВАТЬ ПО
НоменклатураСсылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДанныеСНоменклатурой.НоменклатураСсылка КАК НоменклатураСсылка,
ДанныеСНоменклатурой.АртикулТЗ КАК АртикулТЗ,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЕдиницыИзмерения.Ссылка) КАК КоличествоЕдиниц
ПОМЕСТИТЬ ПозицииСНесколькимиЕдиницами
ИЗ
ДанныеСНоменклатурой КАК ДанныеСНоменклатурой
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
ПО (ЕдиницыИзмерения.Владелец = ДанныеСНоменклатурой.НоменклатураСсылка)
И (НЕ ЕдиницыИзмерения.ПометкаУдаления)
СГРУППИРОВАТЬ ПО
ДанныеСНоменклатурой.НоменклатураСсылка,
ДанныеСНоменклатурой.АртикулТЗ
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЕдиницыИзмерения.Ссылка) > 1
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПозицииСНесколькимиЕдиницами.АртикулТЗ КАК АртикулТЗ,
ПозицииСНесколькимиЕдиницами.НоменклатураСсылка КАК Номенклатура,
ПозицииСНесколькимиЕдиницами.КоличествоЕдиниц КАК КоличествоЕдиниц
ИЗ
ПозицииСНесколькимиЕдиницами КАК ПозицииСНесколькимиЕдиницами
Когда конечной целью является получение одной строки по номенклатуре с флагами вроде ЕстьКоробка или ЕстьПаллета, группировка с подсчетом количества строк требует дополнительных соединений. Гораздо эффективнее применить условное агрегирование с помощью функции МАКСИМУМ(ВЫБОР ...).
Выясним причину, почему это работает: в языке запросов 1С булево значение ИСТИНА старше (больше), чем ЛОЖЬ. Соответственно, если хотя бы для одной подчиненной единицы условие выполнится, агрегатная функция МАКСИМУМ зафиксирует ИСТИНА на уровне всей группы.
Рассмотрим подробнее текст запроса:
ВЫБРАТЬ
ДанныеСНоменклатурой.АртикулТЗ КАК АртикулТЗ,
ДанныеСНоменклатурой.НоменклатураСсылка КАК НоменклатураСсылка,
ДанныеСНоменклатурой.ДлинаШт КАК ДлинаШт,
ДанныеСНоменклатурой.ДлинаКор КАК ДлинаКор,
ДанныеСНоменклатурой.ДлинаПал КАК ДлинаПал,
МАКСИМУМ(ВЫБОР
КОГДА ЕдиницыИзмерения.ЕдиницаПоКлассификатору.Код = "123"
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ) КАК ЕстьПаллета,
МАКСИМУМ(ВЫБОР
КОГДА ЕдиницыИзмерения.ЕдиницаПоКлассификатору.Код = "124"
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ) КАК ЕстьКоробка,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЕдиницыИзмерения.Ссылка) КАК ВсегоЕдиниц
ИЗ
ДанныеСНоменклатурой КАК ДанныеСНоменклатурой
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
ПО (ЕдиницыИзмерения.Владелец = ДанныеСНоменклатурой.НоменклатураСсылка)
И (НЕ ЕдиницыИзмерения.ПометкаУдаления)
СГРУППИРОВАТЬ ПО
ДанныеСНоменклатурой.АртикулТЗ,
ДанныеСНоменклатурой.НоменклатураСсылка,
ДанныеСНоменклатурой.ДлинаШт,
ДанныеСНоменклатурой.ДлинаКор,
ДанныеСНоменклатурой.ДлинаПал
При выполнении подобных операций в боевых базах важно учитывать следующие нюансы:
Артикул не всегда уникален. При наличии дублей в справочнике Номенклатура соединение приведет к умножению строк. Рекомендуется проверять входной массив данных на уникальность сопоставляемых полей.ИНДЕКСИРОВАТЬ ПО для полей временных таблиц, которые в дальнейшем участвуют в секциях ПО и ГДЕ.НайтиСтроки в цикле по большой таблице значений работает значительно медленнее, чем единый пакетный запрос, выполняемый на стороне СУБД.