При работе с запросами в 1С:Предприятие программисты часто сталкиваются с ситуацией, когда условия фильтрации начинают работать «не так, как ожидалось». Особенно это заметно при работе с иерархическими справочниками, такими как Номенклатура. Типичные проблемы включают в себя попадание в выборку помеченных на удаление объектов и «исчезновение» групп, в которых нет подходящих элементов. В этой статье мы подробно разберем, как правильно выстраивать логику условий и соединений, чтобы получить корректный результат.
Разберем ситуацию, когда в запросе используется фильтрация по пометке удаления и одновременно проверяется принадлежность к нескольким уровням иерархии через оператор ИЛИ. Рассмотрим пример ошибочного кода:
ГДЕ
_Таблица.ЭтоГруппа = ЛОЖЬ
И _Таблица.ПометкаУдаления = ЛОЖЬ
И _Таблица.Ссылка.Родитель = &Родитель
ИЛИ _Таблица.Ссылка.Родитель.Родитель = &Родитель
ИЛИ _Таблица.Ссылка.Родитель.Родитель.Родитель = &Родитель
Проанализируем причину ошибки. В языке запросов 1С (как и в SQL) логическое И имеет более высокий приоритет, чем ИЛИ. В данном примере система сначала проверяет первые три условия (что это не группа, нет пометки удаления и родитель совпадает), а затем «приклеивает» к результату все записи, у которых Родитель.Родитель совпадает с параметром, невзирая на пометку удаления или флаг ЭтоГруппа.
Чтобы исправить ситуацию, необходимо явно сгруппировать условия с помощью скобок:
ГДЕ
_Таблица.ЭтоГруппа = ЛОЖЬ
И _Таблица.ПометкаУдаления = ЛОЖЬ
И (_Таблица.Ссылка.Родитель = &Родитель
ИЛИ _Таблица.Ссылка.Родитель.Родитель = &Родитель
ИЛИ _Таблица.Ссылка.Родитель.Родитель.Родитель = &Родитель)
Теперь условия ПометкаУдаления = ЛОЖЬ и ЭтоГруппа = ЛОЖЬ будут применяться ко всем веткам иерархии, перечисленным в скобках. Это классический пример, который учит нас всегда контролировать приоритеты операций.
Использование конструкции Родитель.Родитель... через точку не только загромождает код, но и ограничивает вложенность справочника. Если в справочнике появится пятый уровень вложенности, ваш запрос перестанет его учитывать. Рассмотрим более универсальный способ — использование оператора В ИЕРАРХИИ.
ГДЕ
_Таблица.ЭтоГруппа = ЛОЖЬ
И _Таблица.ПометкаУдаления = ЛОЖЬ
И _Таблица.Ссылка В ИЕРАРХИИ(&Родитель)
Этот вариант делает код чище и гибче. Однако стоит помнить, что на уровне СУБД оператор В ИЕРАРХИИ может работать медленнее на огромных справочниках, так как он преобразуется в сложный подзапрос. В большинстве случаев для справочника номенклатуры это оптимальный выбор.
Часто бизнес-задача звучит так: «Посчитать количество товаров в каждой папке, включая папки, где товаров нет». Если мы просто напишем ГДЕ ПометкаУдаления = ЛОЖЬ, то группы, в которых все элементы помечены на удаление (или их просто нет), исчезнут из результата, так как они не пройдут фильтр.
Чтобы решить эту задачу, мы должны использовать Левое соединение. Разберем алгоритм действий:
ГДЕ в секцию ПО (условие связи).Если оставить условие ГДЕ НоменклатураЭлемент.ПометкаУдаления = ЛОЖЬ, то левое соединение превратится во внутреннее, и пустые группы пропадут. Проанализируем правильную структуру запроса:
ВЫБРАТЬ
Группы.Ссылка КАК Группа,
КОЛИЧЕСТВО(Элементы.Ссылка) КАК КоличествоТоваров
ИЗ
Справочник.Номенклатура КАК Группы
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Элементы
ПО Группы.Ссылка = Элементы.Родитель
И Элементы.ЭтоГруппа = ЛОЖЬ
И Элементы.ПометкаУдаления = ЛОЖЬ
ГДЕ
Группы.ЭтоГруппа = ИСТИНА
И Группы.Ссылка В ИЕРАРХИИ(&Родитель)
СГРУППИРОВАТЬ ПО
Группы.Ссылка
При использовании левого соединения для подсчета количества важно помнить два момента:
КОЛИЧЕСТВО(Поле) игнорирует значения NULL. Если для какой-то группы не нашлось элементов, поле Элементы.Ссылка будет содержать NULL, и КОЛИЧЕСТВО вернет 0. Это именно то, что нам нужно.ЕСТЬNULL. Например: СУММА(ЕСТЬNULL(ВТ.Количество, 0)). Это гарантирует, что пустые значения не приведут к ошибке расчета всей суммы.Также стоит обратить внимание на проверку пометки удаления у самих групп. Если папка помечена на удаление, то обычно её содержимое также не должно учитываться. В таком случае условие Группы.ПометкаУдаления = ЛОЖЬ следует добавить в секцию ГДЕ.
Если вы работаете с очень большими объемами данных и используете временные таблицы (ПОМЕСТИТЬ ВТ), не забывайте про индексацию. Рассмотрим пример хорошего тона:
ВЫБРАТЬ
Ссылка КАК Родитель
ПОМЕСТИТЬ ВТ_Группы
ИЗ
Справочник.Номенклатура
ГДЕ
ЭтоГруппа = ИСТИНА
И Ссылка В ИЕРАРХИИ(&Родитель)
ИНДЕКСИРОВАТЬ ПО
Родитель
;
Индексирование поля Родитель во временной таблице существенно ускорит последующее соединение со справочником при подсчете элементов. Это критически важно в высоконагруженных системах или при формировании тяжелых аналитических отчетов.
Таким образом, для корректного решения задачи мы должны: правильно расставлять скобки в логических выражениях, использовать В ИЕРАРХИИ для работы с произвольной вложенностью и применять ЛЕВОЕ СОЕДИНЕНИЕ с условиями в секции ПО для сохранения структуры справочника в отчете.