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