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