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