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