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