К статье
Практический материал

Разработка и Конфигурация · 14 мин

Как обойти табличную часть из запроса в 1С

Как обойти табличную часть документа через запрос в 1С: Выбрать(), обход по группировкам, итоги, соединение с шапкой. Примеры кода на встроенном языке.

Задача обойти табличную часть документа через запрос возникает постоянно: при формировании печатных форм, проведении документов, выгрузке данных и проверках заполнения. В отличие от прямого перебора коллекции Документ.Товары через цикл Для Каждого, запрос позволяет получить только нужные строки, сразу сгруппировать их, посчитать суммы и связать с данными шапки — без лишних обращений к базе.

Ключевая идея проста: табличная часть в запросе — это обычная виртуальная таблица с именем вида Документ.РеализацияТоваровУслуг.Товары. Из неё делается выборка, а дальше результат обходится стандартными средствами объекта РезультатЗапроса: методом Выбрать() или выгрузкой в таблицу значений через Выгрузить().

Ниже разобраны оба подхода с рабочим кодом, варианты обхода с группировками и итогами, соединение табличной части с шапкой документа, а также типичные ошибки, из-за которых запрос возвращает не те строки или падает с ошибкой.

Как устроена табличная часть в языке запросов

Каждая табличная часть документа или справочника хранится в базе как отдельная таблица, связанная с шапкой по полю Ссылка. В запросе к ней обращаются через точку: Документ.РеализацияТоваровУслуг.Товары. Поля табличной части — это реквизиты, которые вы видите в конфигураторе, плюс служебное поле НомерСтроки.

Важно понимать: запрос к табличной части без отбора по ссылке вернёт строки всех документов этого вида в базе. На больших базах это десятки миллионов записей и гарантированная проблема с производительностью. Поэтому отбор ГДЕ Товары.Ссылка = &Ссылка — обязательный элемент, а не рекомендация.

Если нужны данные нескольких документов сразу (например, печать списка накладных), отбор делают по списку ссылок через В (&СписокСсылок), передавая в параметр массив или список значений.

Простейший обход табличной части одного документа

Базовый сценарий: есть ссылка на документ, нужно перебрать строки его табличной части и что-то с ними сделать — например, проверить заполнение или сформировать движения. Запрос выбирает нужные поля, результат обходится выборкой.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Номенклатура КАК Номенклатура,

| Товары.Количество КАК Количество,

| Товары.Цена КАК Цена,

| Товары.Сумма КАК Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

|ГДЕ

| Товары.Ссылка = &Ссылка

|УПОРЯДОЧИТЬ ПО

| Товары.НомерСтроки";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Строка(Выборка.Номенклатура) + " — " + Выборка.Количество);

КонецЦикла;

Обратите внимание на УПОРЯДОЧИТЬ ПО Товары.НомерСтроки. Без упорядочивания СУБД не гарантирует порядок строк, и строки в выборке могут идти не так, как в документе. Для печатных форм это критично — нумерация строк на бумаге «поедет».

Выборка через Выбрать() — однопроходная: двигаться можно только вперёд, методом Следующий(). Если нужно несколько раз пройтись по строкам или обращаться к ним по индексу, используйте Выгрузить(), о котором ниже.

Порядок действий
  1. 1
    Сформулируйте текст запроса к таблице Документ.Вид.ТабличнаяЧасть
  2. 2
    Добавьте отбор ГДЕ Ссылка = &Ссылка и упорядочивание по НомерСтроки
  3. 3
    Установите параметры через УстановитьПараметр()
  4. 4
    Выполните запрос и получите выборку методом Выбрать()
  5. 5
    Обойдите выборку циклом Пока Выборка.Следующий() Цикл
  6. 6
    Проверьте результат на документе с несколькими строками и на пустой табличной части

Обход через выгрузку в таблицу значений

Метод Выгрузить() помещает результат запроса в таблицу значений в памяти. Это удобно, когда нужен произвольный доступ к строкам, повторный обход, поиск по строкам или сортировка уже после выполнения запроса.

ТаблицаТоваров = Запрос.Выполнить().Выгрузить();

Для Каждого СтрокаТовара Из ТаблицаТоваров Цикл

// произвольная логика обработки строки

КонецЦикла;

// Поиск конкретной строки

Найденные = ТаблицаТоваров.НайтиСтроки(

Новый Структура("Номенклатура", ИскомаяНоменклатура));

Разница в потреблении ресурсов: выборка читает данные порциями и держит соединение с результатом запроса, таблица значений загружает всё в память сразу. Для табличной части одного документа разницы практически нет — строк немного. Для выборки по тысячам документов таблица значений может занять заметный объём памяти.

Выбрать() или Выгрузить()

Выбрать() — выборка

Подходит для однопроходного обхода и больших результатов. Меньше памяти, но движение только вперёд и нет поиска по строкам.

Выгрузить() — таблица значений

Подходит, когда нужен повторный обход, НайтиСтроки(), сортировка или обращение по индексу. Требует памяти под весь результат.

Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией)

Подходит для дерева: документы с вложенными строками табличных частей при печати списка документов.

Табличная часть вместе с данными шапки

На практике строк почти никогда не бывает достаточно — нужны контрагент, дата, номер, организация из шапки. Есть два подхода: соединение через ЛЕВОЕ СОЕДИНЕНИЕ и обращение через точку к полям ссылки.

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Ссылка.Номер КАК Номер,

| Товары.Ссылка.Дата КАК Дата,

| Товары.Ссылка.Контрагент КАК Контрагент,

| Товары.Номенклатура КАК Номенклатура,

| Товары.Сумма КАК Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

|ГДЕ

| Товары.Ссылка = &Ссылка";

Обращение Товары.Ссылка.Контрагент выглядит коротко, но физически это то же соединение с таблицей шапки. Если полей шапки много, читабельнее явное соединение:

"ВЫБРАТЬ

| Реализация.Номер,

| Реализация.Контрагент,

| Товары.Номенклатура,

| Товары.Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

| ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация

| ПО Товары.Ссылка = Реализация.Ссылка

|ГДЕ

| Товары.Ссылка = &Ссылка"

Левое соединение здесь безопасно: у строки табличной части всегда есть документ-владелец, поэтому соединение фактически работает как внутреннее. Но левое соединение защищает от потери строк в нештатных ситуациях с битыми ссылками.

Обход с группировками: шапка и строки нескольких документов

При печати списка документов или формировании реестра нужен двухуровневый обход: сначала документ, потом его строки. Это делается через итоги по группировке Ссылка и обход выборки по группировкам.

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Ссылка КАК Документ,

| Товары.Номенклатура КАК Номенклатура,

| Товары.Сумма КАК Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

|ГДЕ

| Товары.Ссылка В (&СписокДокументов)

|ИТОГИ

| СУММА(Сумма)

|ПО

| Документ";

Запрос.УстановитьПараметр("СписокДокументов", МассивСсылок);

ВыборкаДокументы = Запрос.Выполнить().Выбрать(

ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаДокументы.Следующий() Цикл

Сообщить("Документ: " + ВыборкаДокументы.Документ

+ ", итого: " + ВыборкаДокументы.Сумма);

ВыборкаСтроки = ВыборкаДокументы.Выбрать();

Пока ВыборкаСтроки.Следующий() Цикл

Сообщить(" " + ВыборкаСтроки.Номенклатура);

КонецЦикла;

КонецЦикла;

Внешний цикл идёт по группировке документов, внутренний — по детальным записям каждого документа. Итог по документу (сумма) доступен уже на уровне группировки, пересчитывать его в цикле не нужно.

Альтернатива — выгрузить результат в дерево значений через Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией). Тогда структура «документ → строки» хранится целиком в памяти и её можно обходить многократно, что удобно при построении сложных печатных форм.

Если данные табличной части нужны только для одной сводной цифры (например, общая сумма документа), не обходите строки вовсе — посчитайте СУММА(Сумма) с группировкой прямо в запросе. Один агрегатный запрос быстрее и короче любого цикла.
ПЗ
Практическое замечание

Группировка строк прямо в запросе

Частая задача — свернуть строки табличной части: например, одинаковая номенклатура встречается в документе несколько раз, а для движений или печати нужна одна строка с суммарным количеством. Делать это в цикле обхода — лишняя работа, запрос справится сам.

"ВЫБРАТЬ

| Товары.Номенклатура КАК Номенклатура,

| СУММА(Товары.Количество) КАК Количество,

| СУММА(Товары.Сумма) КАК Сумма

|ИЗ

| Документ.РеализацияТоваровУслуг.Товары КАК Товары

|ГДЕ

| Товары.Ссылка = &Ссылка

|СГРУППИРОВАТЬ ПО

| Товары.Номенклатура"

Обход такого результата ничем не отличается от обычного — та же выборка и цикл Пока Выборка.Следующий(). Но строк в выборке уже меньше, и логика свёртки не загромождает код модуля.

Учтите нюанс: при группировке теряется НомерСтроки и связь с конкретными строками документа. Если вам нужно потом сообщить пользователю, в какой именно строке ошибка, — группировка в запросе не подходит, обходите строки как есть и агрегируйте через соответствие.

Типичные ошибки и ограничения

Самая частая ошибка — забыть отбор по ссылке и получить строки всех документов вида. Вторая по популярности — обращение к полю выборки, которого нет в списке выбранных полей: платформа выдаст ошибку «Поле не найдено». Имена полей в выборке совпадают с псевдонимами (КАК) из текста запроса.

Третья группа проблем — с пустым результатом. Если документ не найден или табличная часть пуста, выборка просто не содержит записей: цикл не выполнится ни разу, и это нормально. Но если код после цикла рассчитывает, что данные есть, добавьте проверку РезультатЗапроса.Пустой или Выборка.Количество().

Отдельно стоит сказать про обход табличной части объекта, который ещё не записан. Запрос читает данные из базы, поэтому для незаписанного документа запрос к табличной части вернёт пустой результат. Если нужно обойти строки открытого на форме документа, используйте прямой перебор коллекции Объект.Товары, а не запрос.

Типичные ошибки
01
Нет отбора ГДЕ Ссылка = &Ссылка — запрос возвращает строки всех документов, обход занимает минуты
02
Запрос к табличной части незаписанного документа — результат пуст, ведь данные ещё не в базе
03
Нет УПОРЯДОЧИТЬ ПО НомерСтроки — строки в выборке идут в произвольном порядке, печатная форма расходится с документом
04
Обращение к полю выборки, не перечисленному в ВЫБРАТЬ — ошибка «Поле не найдено» при обходе
05
Повторный обход одной и той же выборки — после первого прохода выборка исчерпана, нужен новый Выбрать() или Выгрузить()
Контрольные точки

Как проверить результат

Количество строк в выборке
Норма

Совпадает с числом строк табличной части документа (или меньше, если есть группировка)

Тревожный признак

Строк больше, чем в документе — вероятно, пропущен отбор по ссылке

Порядок строк
Норма

Совпадает с порядком в документе при упорядочивании по НомерСтроки

Тревожный признак

Строки перемешаны — нет упорядочивания

Суммы по группировке
Норма

Итог по документу равен сумме по строкам документа

Тревожный признак

Итог отличается — проверяйте условия соединений и группировку

Когда запрос не нужен: прямой обход коллекции

Запрос к табличной части оправдан, когда данные уже в базе, нужен отбор, группировка, соединение с другими таблицами или обработка многих документов сразу. Но если у вас уже есть объект документа в памяти — например, в обработчике формы или в модуле объекта при записи — проще и быстрее обойти коллекцию напрямую:

Для Каждого СтрокаТовара Из Объект.Товары Цикл

Если Не ЗначениеЗаполнено(СтрокаТовара.Номенклатура) Тогда

// обработка ошибки заполнения

КонецЕсли;

КонецЦикла;

Прямой обход не требует обращения к базе и работает с актуальным состоянием объекта, включая незаписанные изменения. Запрос же всегда видит только то, что записано. Смешивать оба подхода в одной логике (часть данных брать запросом, часть из объекта) — частый источник трудноуловимых расхождений.

Маршрут решения

Как выбрать способ обхода

Документ открыт на форме или изменяется в модуле объектаОбходите коллекцию Объект.Товары напрямую — запрос не увидит незаписанные данные
Нужны строки одного записанного документа с полями шапкиЗапрос к табличной части с отбором по Ссылка и обход выборкой
Нужно свернуть дубли номенклатуры или посчитать итогиГруппировка СГРУППИРОВАТЬ ПО прямо в запросе, обход результата выборкой
Нужно вывести список документов с их строкамиИТОГИ ПО Ссылка и обход Выбрать(ПоГруппировкам) с вложенным циклом
Нужен повторный доступ к строкам или поискВыгрузите результат в таблицу значений методом Выгрузить()

FAQ

Как обойти табличную часть сразу нескольких документов?

Передайте в параметр массив или список значений со ссылками и используйте условие Товары.Ссылка В (&СписокДокументов). Для структурированного обхода добавьте ИТОГИ ПО Ссылка и обходите результат выборкой по группировкам с вложенным циклом по детальным записям.

Почему запрос к табличной части возвращает пустой результат?

Чаще всего документ ещё не записан в базу — запрос видит только сохранённые данные. Также проверьте, что переданная в параметр ссылка именно того вида документа, к которому обращается запрос, и что табличная часть в документе действительно заполнена.

Как сохранить порядок строк как в документе?

Добавьте в запрос УПОРЯДОЧИТЬ ПО Товары.НомерСтроки. Поле НомерСтроки есть у каждой табличной части. Без явного упорядочивания СУБД не гарантирует порядок записей, и строки могут выводиться в произвольной последовательности.

Можно ли в запросе обратиться к реквизиту номенклатуры из строки?

Да, через точку: Товары.Номенклатура.Артикул. Но каждое такое обращение — неявное соединение со справочником. Если нужно много полей номенклатуры, эффективнее явное левое соединение со справочником по условию Товары.Номенклатура = Номенклатура.Ссылка.

Что быстрее: запрос к табличной части или получение объекта и обход коллекции?

Если объект ещё не загружен, запрос обычно выгоднее: он забирает только нужные поля, тогда как ПолучитьОбъект() читает документ целиком со всеми табличными частями. Если объект уже загружен в память, прямой обход коллекции быстрее, так как не требует обращения к базе.

Как обойти табличную часть справочника, а не документа?

Точно так же — таблица называется Справочник.ИмяСправочника.ИмяТабличнойЧасти, отбор по полю Ссылка. Вся логика обхода выборкой, группировок и соединений идентична документам.

Итог

Что важно запомнить

Табличная часть в запросе — таблица Документ.Вид.ИмяТабличнойЧасти с обязательным отбором по Ссылка. Обход выполняется выборкой Пока Выборка.Следующий() или через Выгрузить() в таблицу значений. Порядок строк обеспечивает УПОРЯДОЧИТЬ ПО НомерСтроки, структуру «документы и их строки» — ИТОГИ ПО Ссылка с обходом по группировкам. Для незаписанного документа запрос не подходит — обходите коллекцию объекта напрямую.