Как в запросе 1С отобрать единицы измерения, имеющие одного владельца?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При загрузке данных из внешних источников (например, таблиц Excel) нередко возникает задача сопоставления упаковок и единиц измерения с уже существующими объектами в базе. Типичный сценарий: необходимо определить позиции номенклатуры, для которых в справочнике ЕдиницыИзмерения создано более одной записи (например, базовая единица «шт», а также дополнительные — «коробка» или «паллета»), и проставить соответствующие признаки в результирующей выборке.

Разберем, почему ручная построчная обработка через НайтиСтроки уступает запросам, какие ошибки часто допускаются при соединении подчиненных справочников и как правильно реализовать выборку с помощью языка запросов 1С.

Анализ проблемы и типичные ошибки проектирования запроса

Проанализируем исходную структуру запроса и выделим моменты, снижающие производительность и точность работы алгоритма:

  1. Обращение через точку в условии соединения: Конструкция ЕдиницыИзмерения.Владелец.Артикул = ДанныеИзТЗ.АртикулТЗ порождает неявное левое соединение со справочником Номенклатура. В таком виде СУБД не может задействовать штатный индекс подчиненного справочника (Владелец, Ссылка), что приводит к медленному сканированию таблиц. Правильный путь — сопоставить артикул напрямую со справочником Номенклатура, получить ссылку, и уже по ней связывать подчиненные единицы.
  2. Избыточное условие НЕ ДанныеИзТЗ.АртикулТЗ ЕСТЬ NULL: При левом соединении таблица ДанныеИзТЗ является ведущей. Если в самой исходной таблице значений нет пустых значений, данная фильтрация не имеет практического смысла.
  3. Дублирование строк: Если у одной позиции номенклатуры задано несколько единиц измерения, простое левое соединение вернет столько строк по одному товару, сколько единиц найдено в базе. Это усложняет дальнейшую обработку загружаемых данных.

Способ 1. Группировка и фильтрация по количеству через секцию ИМЕЮЩИЕ

Рассмотрим классический способ решения задачи средствами языка запросов. Если нам необходимо выделить только ту номенклатуру, у которой зарегистрировано более одной единицы измерения, применим группировку по владельцу с агрегатной функцией КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...) и условием ИМЕЮЩИЕ.

Разберем по шагам создание такого запроса:

  1. Помещаем загруженную таблицу значений во временную таблицу и обязательно индексируем поле сопоставления.
  2. Связываем временную таблицу со справочником Номенклатура по артикулу.
  3. Связываем полученную номенклатуру с подчиненным справочником ЕдиницыИзмерения.
  4. Группируем результат и отсекаем позиции, у которых количество единиц не превышает 1.

Посмотрим на пример реализации данного запроса:


ВЫБРАТЬ
    ТЗ_Excel.ДлинаШт КАК ДлинаШт,
    ТЗ_Excel.ДлинаКор КАК ДлинаКор,
    ТЗ_Excel.ДлинаПал КАК ДлинаПал,
    ВЫРАЗИТЬ(ТЗ_Excel.Артикул КАК СТРОКА(25)) КАК АртикулТЗ
ПОМЕСТИТЬ ДанныеИзТЗ
ИЗ
    &ТЗ_Excel КАК ТЗ_Excel

ИНДЕКСИРОВАТЬ ПО
    АртикулТЗ
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ДанныеИзТЗ.АртикулТЗ КАК АртикулТЗ,
    Номенклатура.Ссылка КАК НоменклатураСсылка,
    ДанныеИзТЗ.ДлинаШт КАК ДлинаШт,
    ДанныеИзТЗ.ДлинаКор КАК ДлинаКор,
    ДанныеИзТЗ.ДлинаПал КАК ДлинаПал
ПОМЕСТИТЬ ДанныеСНоменклатурой
ИЗ
    ДанныеИзТЗ КАК ДанныеИзТЗ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
        ПО (ДанныеИзТЗ.АртикулТЗ = Номенклатура.Артикул)
            И (НЕ Номенклатура.ЭтоГруппа)
            И (НЕ Номенклатура.ПометкаУдаления)

ИНДЕКСИРОВАТЬ ПО
    НоменклатураСсылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ДанныеСНоменклатурой.НоменклатураСсылка КАК НоменклатураСсылка,
    ДанныеСНоменклатурой.АртикулТЗ КАК АртикулТЗ,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЕдиницыИзмерения.Ссылка) КАК КоличествоЕдиниц
ПОМЕСТИТЬ ПозицииСНесколькимиЕдиницами
ИЗ
    ДанныеСНоменклатурой КАК ДанныеСНоменклатурой
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
        ПО (ЕдиницыИзмерения.Владелец = ДанныеСНоменклатурой.НоменклатураСсылка)
            И (НЕ ЕдиницыИзмерения.ПометкаУдаления)
СГРУППИРОВАТЬ ПО
    ДанныеСНоменклатурой.НоменклатураСсылка,
    ДанныеСНоменклатурой.АртикулТЗ
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЕдиницыИзмерения.Ссылка) > 1
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ПозицииСНесколькимиЕдиницами.АртикулТЗ КАК АртикулТЗ,
    ПозицииСНесколькимиЕдиницами.НоменклатураСсылка КАК Номенклатура,
    ПозицииСНесколькимиЕдиницами.КоличествоЕдиниц КАК КоличествоЕдиниц
ИЗ
    ПозицииСНесколькимиЕдиницами КАК ПозицииСНесколькимиЕдиницами

Способ 2. Получение флагов наличия единиц через условную агрегацию

Когда конечной целью является получение одной строки по номенклатуре с флагами вроде ЕстьКоробка или ЕстьПаллета, группировка с подсчетом количества строк требует дополнительных соединений. Гораздо эффективнее применить условное агрегирование с помощью функции МАКСИМУМ(ВЫБОР ...).

Выясним причину, почему это работает: в языке запросов 1С булево значение ИСТИНА старше (больше), чем ЛОЖЬ. Соответственно, если хотя бы для одной подчиненной единицы условие выполнится, агрегатная функция МАКСИМУМ зафиксирует ИСТИНА на уровне всей группы.

Рассмотрим подробнее текст запроса:


ВЫБРАТЬ
    ДанныеСНоменклатурой.АртикулТЗ КАК АртикулТЗ,
    ДанныеСНоменклатурой.НоменклатураСсылка КАК НоменклатураСсылка,
    ДанныеСНоменклатурой.ДлинаШт КАК ДлинаШт,
    ДанныеСНоменклатурой.ДлинаКор КАК ДлинаКор,
    ДанныеСНоменклатурой.ДлинаПал КАК ДлинаПал,
    МАКСИМУМ(ВЫБОР
            КОГДА ЕдиницыИзмерения.ЕдиницаПоКлассификатору.Код = "123"
                ТОГДА ИСТИНА
            ИНАЧЕ ЛОЖЬ
        КОНЕЦ) КАК ЕстьПаллета,
    МАКСИМУМ(ВЫБОР
            КОГДА ЕдиницыИзмерения.ЕдиницаПоКлассификатору.Код = "124"
                ТОГДА ИСТИНА
            ИНАЧЕ ЛОЖЬ
        КОНЕЦ) КАК ЕстьКоробка,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЕдиницыИзмерения.Ссылка) КАК ВсегоЕдиниц
ИЗ
    ДанныеСНоменклатурой КАК ДанныеСНоменклатурой
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ЕдиницыИзмерения КАК ЕдиницыИзмерения
        ПО (ЕдиницыИзмерения.Владелец = ДанныеСНоменклатурой.НоменклатураСсылка)
            И (НЕ ЕдиницыИзмерения.ПометкаУдаления)
СГРУППИРОВАТЬ ПО
    ДанныеСНоменклатурой.АртикулТЗ,
    ДанныеСНоменклатурой.НоменклатураСсылка,
    ДанныеСНоменклатурой.ДлинаШт,
    ДанныеСНоменклатурой.ДлинаКор,
    ДанныеСНоменклатурой.ДлинаПал

Рекомендации по оптимизации работы с подчиненными справочниками

При выполнении подобных операций в боевых базах важно учитывать следующие нюансы:

  1. Контроль уникальности артикулов: В типовых конфигурациях реквизит Артикул не всегда уникален. При наличии дублей в справочнике Номенклатура соединение приведет к умножению строк. Рекомендуется проверять входной массив данных на уникальность сопоставляемых полей.
  2. Индексация временных таблиц: Всегда используйте конструкцию ИНДЕКСИРОВАТЬ ПО для полей временных таблиц, которые в дальнейшем участвуют в секциях ПО и ГДЕ.
  3. Отказ от ручной фильтрации в коде: Метод НайтиСтроки в цикле по большой таблице значений работает значительно медленнее, чем единый пакетный запрос, выполняемый на стороне СУБД.
← На главную