При работе в среде 1С:Предприятие часто возникает задача: в форме списка или журнале документов (например, «Поступление товаров и услуг») быстро отобрать только те документы, в которых присутствует определенная номенклатура — для этого подойдёт панель быстрых фильтров для списков 1С. Поскольку номенклатура хранится в табличной части, а стандартные отборы динамического списка работают только по реквизитам шапки, эта задача требует особого технического подхода. Если решать её неоптимально, можно столкнуться с резким падением производительности СУБД и зависанием системы.
В этой статье мы подробно разберем несколько профессиональных способов решения данной задачи для управляемых форм — от простых программных методов до высокопроизводительных архитектурных решений платформы 1С.
---
Рассмотрим самый очевидный способ, который часто выбирают разработчики. Его суть заключается в следующем: мы добавляем на форму списка документов поле выбора номенклатуры. При изменении этого поля мы делаем запрос к табличной части документов, выбираем все документы, где встречается данный товар, выгружаем их в массив и устанавливаем этот массив в качестве отбора для динамического списка.
Разберем этот процесс по шагам. Создадим на форме списка реквизит ОтборТовар с типом ссылки на справочник Номенклатура и вынесем его на форму. Для события ПриИзменении этого поля напишем следующий код:
&НаКлиенте
Процедура ОтборТоварПриИзменении(Элемент)
УстановитьОтборПоТоваруНаСервере();
КонецПроцедуры
&НаСервере
Процедура УстановитьОтборПоТоваруНаСервере()
// Очищаем предыдущий отбор по ссылке, если он был установлен
ОбщегоНазначенияКлиентСервер.УдалитьЭлементыОтбораДинамическогоСписка(Список, "Ссылка");
Если ЗначениеЗаполнено(ОтборТовар) Тогда
// Шаг 1. Получаем ссылки на документы, содержащие выбранную номенклатуру
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ТабЧасть.Ссылка КАК Ссылка
|ИЗ
| Документ.ПоступлениеТоваровУслуг.Товары КАК ТабЧасть
|ГДЕ
| ТабЧасть.Номенклатура = &Номенклатура";
Запрос.УстановитьПараметр("Номенклатура", ОтборТовар);
ТаблицаСсылок = Запрос.Выполнить().Выгрузить();
МассивСсылок = ТаблицаСсылок.ВыгрузитьКолонку("Ссылка");
// Шаг 2. Устанавливаем отбор в динамическом списке по массиву полученных ссылок
Если МассивСсылок.Количество() > 0 Тогда
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список,
"Ссылка",
МассивСсылок,
РежимСравненияВыбораИспользования.ВВыборе
);
Иначе
// Если документов с таким товаром нет, передаем пустой массив, чтобы список очистился
ПустойМассив = Новый Массив;
ПустойМассив.Добавить(Документы.ПоступлениеТоваровУслуг.ПустаяСсылка());
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список,
"Ссылка",
ПустойМассив,
РежимСравненияВыбораИспользования.ВВыборе
);
EndIf;
КонецЕсли;
КонецПроцедуры
Важный нюанс и проблемы производительности: Этот метод прост в реализации, однако он таит в себе серьезную опасность при работе в реальных базах данных. Если выбранный товар является популярным (например, упаковочный пакет в рознице) и содержится в десятках тысяч документов, то полученный массив ссылок будет огромным. Передача списка из более чем 1000–5000 элементов в оператор IN (...) SQL-запроса приводит к катастрофическому замедлению работы СУБД, а в некоторых случаях вызывает аварийное завершение работы сервера SQL из-за переполнения параметров — такие проблемы выявляет анализ и оптимизация производительности запросов 1С.
---
Чтобы избежать передачи тяжелых массивов данных между сервером приложений и СУБД, проанализируем альтернативный вариант — использование возможностей самого динамического списка. Вместо того чтобы выгружать ссылки на клиенте или сервере в массив, мы можем передать в динамический список один-единственный параметр — выбранную номенклатуру, а всю логику фильтрации переложить на СУБД.
Разберем по шагам, как это настроить:
Список) и установим флаг «Произвольный запрос».В (ВЫБРАТЬ...) с проверкой на пустое значение параметра:
ВЫБРАТЬ
ДокументТаблица.Ссылка КАК Ссылка,
ДокументТаблица.Дата КАК Дата,
ДокументТаблица.Номер КАК Номер,
ДокументТаблица.Проведен КАК Проведен,
ДокументТаблица.СуммаДокумента КАК СуммаДокумента
ИЗ
Документ.ПоступлениеТоваровУслуг КАК ДокументТаблица
ГДЕ
(&ОтборНоменклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
ИЛИ ДокументТаблица.Ссылка В
(ВЫБРАТЬ
ТЧ.Ссылка
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК ТЧ
ГДЕ
ТЧ.Номенклатура = &ОтборНоменклатура))
Теперь при изменении реквизита отбора на форме нам не нужно делать промежуточные запросы в коде. Достаточно просто передать значение параметра в динамический список:
&НаСервере
Процедура УстановитьПараметрДинамическогоСписка()
ЗначениеПараметра = ?(ЗначениеЗаполнено(ОтборТовар), ОтборТовар, Справочники.Номенклатура.ПустаяСсылка());
Список.Параметры.УстановитьЗначениеПараметра("ОтборНоменклатура", ЗначениеПараметра);
КонецПроцедуры
При таком подходе СУБД сама строит оптимальный план выполнения запроса, используя внутренние механизмы и индексы табличных частей. Производительность этого решения значительно выше первого варианта.
---
Если мы попытаемся сделать левое соединение (LEFT JOIN) таблицы документа с таблицей товаров в обычном запросе динамического списка для получения поля отбора, то при просмотре списка строки документов начнут дублироваться (каждая строка документа будет выведена столько раз, сколько различных товаров присутствует в его табличной части). Использование оператора РАЗЛИЧНЫЕ решит проблему дубликатов, но снизит производительность на больших объемах данных.
Правильный профессиональный подход заключается в тонкой настройке Системы компоновки данных (СКД), которая управляет выборкой в динамическом списке:
Товары.Номенклатура и добавим его исключительно в раздел **«Условия»**.Благодаря этому трюку поле «Номенклатура» появится у пользователя в стандартных настройках отбора («Еще -> Настроить список -> Отбор») — также есть готовое расширение настройки отборов в списках 1С. При обычном открытии формы без установленного отбора СУБД будет выполнять легкий стандартный запрос без каких-либо соединений с табличными частями. Соединение таблиц и фильтрация произойдут только тогда, когда пользователь непосредственно активирует данный отбор. При этом дублирования строк на экране происходить не будет.
---
Выясним, как решить задачу с помощью штатного механизма платформы, созданного специально для таких сценариев — объекта метаданных «Критерии отбора» (Selection Criteria).
Рассмотрим, как его настроить в конфигураторе:
СвязанныеДокументыПоНоменклатуре.Номенклатура.Документ.ПоступлениеТоваровУслуг.Товары.Реквизит.Номенклатура, Документ.РеализацияТоваровУслуг.Товары.Реквизит.Номенклатура и т.д.Преимущества использования критериев отбора:
&НаКлиенте
Процедура ПоказатьДокументыПоТовару(ТоварСсылка)
ПараметрыОтбора = Новый Структура("Значение", ТоварСсылка);
ОткрытьФорму("КритерийОтбора.СвязанныеДокументыПоНоменклатуре.ФормаСписка", ПараметрыОтбора);
КонецПроцедуры
---
В высоконагруженных типовых конфигурациях 1С (например, в решениях на базе БСП, таких как ЗУП 3.1) разработчики используют метод денормализации данных. Чтобы не обращаться к тяжелым табличным частям при каждом просмотре списка, в шапку документа добавляют специальный служебный строковый реквизит (например, КраткийСоставДокумента).
При записи документа этот реквизит программно заполняется строковым перечислением элементов (например, первыми буквами фамилий сотрудников или ключевыми наименованиями номенклатуры). В форме списка поиск и отбор осуществляются по этому текстовому реквизиту шапки. Это существенно повышает скорость работы интерфейса, однако имеет ограничение по длине строки (как правило, длина ограничивается 100-150 символами, поэтому все элементы табличной части туда могут не поместиться).
---
Если в вашей конфигурации внедрена **Библиотека стандартных подсистем (БСП)**, вам практически не требуется писать собственный сложный код для поиска по табличным частям. В БСП встроен мощный механизм полнотекстового поиска.
Если в базе настроено и регулярно обновляется полнотекстовое индексирование, пользователь может просто ввести наименование товара (или его артикул) в стандартную строку поиска, расположенную над списком документов — ускорить поиск поможет улучшение поиска и автоподбор в 1С. Платформа самостоятельно проанализирует индексы табличных частей и мгновенно выведет на экран те документы, где встречается искомое значение.
---
Мы проанализировали несколько вариантов реализации отбора документов по номенклатуре из табличной части. Для простых и средних баз данных наиболее изящным и быстрым решением является **Способ 3** (настройка динамического списка через вкладку «Условия» в схеме компоновки). Если же требуется осуществлять поиск по множеству различных видов документов одновременно, то наилучшим решением будет использование штатного механизма **«Критерии отбора»** (Способ 4), так как он обеспечивает максимальную производительность на уровне СУБД.