В процессе разработки на платформе 1С:Предприятие часто возникает задача построения дерева связей между документами. Нам может потребоваться найти как «родителей» (документы-основания), так и «детей» (подчиненные документы). Реализация этой задачи имеет свои нюансы, связанные с производительностью системы и ограничениями языка запросов. Разберем по шагам, как эффективно решать эти задачи, проанализируем стандартные инструменты и способы оптимизации кода.
Стандартный механизм для поиска связей в 1С — это Критерии отбора. В типовых конфигурациях (таких как УТ, ERP, БП) обычно уже существует критерий отбора СвязанныеДокументы. Рассмотрим, как работать с ним в запросе. Основная особенность заключается в том, что таблица критерия отбора является виртуальной и требует передачи параметра — ссылки на объект, для которого мы ищем связи.
Проанализируем пример простого запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СвязанныеДокументы.Ссылка КАК ПодчиненныйДокумент
|ИЗ
| КритерийОтбора.СвязанныеДокументы(&ДокОснование) КАК СвязанныеДокументы";
Запрос.УстановитьПараметр("ДокОснование", СсылкаНаОбъект);
Результат = Запрос.Выполнить();
Важно понимать ограничение: в параметр &ДокОснование можно передать только одну конкретную ссылку. Если нам нужно получить подчиненные документы сразу для целого списка реализаций или заказов, использование критерия отбора в цикле (как предложено в сообщении №1) приведет к серьезным проблемам с производительностью. Рассмотрим, почему это происходит.
На уровне СУБД обращение к критерию отбора превращается в массивный UNION ALL по всем таблицам документов, которые входят в состав этого критерия. Если в критерий включено 50 видов документов, система будет опрашивать все 50 таблиц. Выполнение такого действия в цикле — верный способ «положить» сервер при большом объеме данных.
Если наша задача — найти документы-основания для выборки, то использовать критерии отбора вовсе не обязательно и даже вредно для скорости. Большинство документов в 1С имеют реквизит, в котором хранится ссылка на родительский объект. Обычно он называется ДокументОснование, Сделка или Заказ.
Разберем ситуацию, когда нам нужно получить основание в одном запросе с основными данными. Выясним причину, почему это работает быстрее: обращение через точку в запросе к реквизиту ссылочного типа использует индекс и выполняется мгновенно.
Пример запроса для получения оснований:
ВЫБРАТЬ
Реализация.Ссылка КАК Ссылка,
Реализация.ЗаказКлиента КАК Основание,
ВЫРАЗИТЬ(Реализация.ЗаказКлиента КАК Документ.ЗаказКлиента).Дата КАК ДатаЗаказа
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Дата МЕЖДУ &ДатаНач И &ДатаКон
Здесь мы напрямую получаем документ-основание. Если тип реквизита составной, рекомендуется использовать функцию ВЫРАЗИТЬ, чтобы платформа могла более эффективно построить план запроса к конкретной таблице.
Рассмотрим ситуацию из сообщения №12: пользователь выбирает имя типа документа из списка (строку), и нам нужно программно построить отчет по его иерархии. Поскольку имя таблицы в тексте запроса не может быть параметром, нам необходимо собрать текст запроса динамически, используя возможности языка 1С.
Посмотрим на пример того, как это можно реализовать:
// Допустим, ВыбранноеИмяДокумента = "ЗаказКлиента"
ТекстЗапроса = "ВЫБРАТЬ Ссылка ИЗ Документ." + ВыбранноеИмяДокумента + " ГДЕ Дата МЕЖДУ &ДатаНач И &ДатаКон";
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("ДатаНач", НачалоПериода);
Запрос.УстановитьПараметр("ДатаКон", КонецПериода);
ТаблицаДокументов = Запрос.Выполнить().Выгрузить();
Чтобы получить подчиненные документы для всех найденных ссылок без использования медленного критерия отбора, мы можем проанализировать метаданные. Проанализируем ситуацию: мы можем найти все документы, у которых есть реквизит, ссылающийся на наш тип документа, и построить один большой запрос с объединениями только по нужным таблицам.
В современных конфигурациях для решения задачи отображения связей существует готовый программный интерфейс. Если ваша конфигурация основана на БСП, мы рекомендуем использовать модуль СтруктураПодчиненности. Это избавит вас от необходимости вручную прописывать логику обхода связей.
Разберем преимущества использования штатных функций:
Rls).Пример вызова команды открытия формы структуры подчиненности из кода:
ПараметрыОткрытия = Новый Структура("ЗначениеКопирования", ОбъектСсылка);
ОткрытьФорму("ОбщаяФорма.СтруктураПодчиненности", ПараметрыОткрытия);
В сообщении №5 автор спрашивает, как избежать «цикла в цикле». Если нам все же крайне необходимо использовать критерий отбора для множества документов, мы можем прибегнуть к соединению с временной таблицей. Хотя КритерийОтбора требует параметр, в некоторых случаях (зависит от версии платформы и СУБД) можно попробовать левое соединение вашей временной таблицы с таблицей критерия.
Однако более надежным и быстрым способом будет следующий алгоритм:
ВТ_Основные.ОБЪЕДИНИТЬ ВСЕ запросов к этим таблицам.ВТ_Основные по полю ДокументОснование.Пример структуры такого «быстрого» запроса:
// 1. Получаем список интересующих нас документов
"ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_Список ИЗ Документ.ЗаказКлиента ГДЕ ... ;"
// 2. Одним махом выбираем всех подчиненных (тех, кто знает о заказе)
"ВЫБРАТЬ
| Реализация.Ссылка КАК Подчиненный,
| ВТ.Ссылка КАК Основание
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Список КАК ВТ ПО Реализация.ЗаказКлиента = ВТ.Ссылка
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Счет.Ссылка,
| ВТ.Ссылка
|ИЗ
| Документ.СчетНаОплатуПокупателю КАК Счет
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Список КАК ВТ ПО Счет.ДокументОснование = ВТ.Ссылка"
Этот подход в десятки раз быстрее работы через КритерийОтбора, так как мы обращаемся напрямую к индексированным полям конкретных таблиц, не заставляя SQL-сервер сканировать лишние данные. Мы надеемся, что эти методы помогут вам строить эффективные и быстрые отчеты по структуре документов в 1С.