Как правильно обращаться к табличным частям документов и справочников в запросах 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

Работа с табличными частями (ТЧ) в системе «1С:Предприятие» — одна из самых частых задач программиста. Будь то создание нового документа на основании существующего, копирование данных из справочника в документ или выгрузка информации во внешнюю систему, нам необходимо уметь эффективно извлекать эти данные с помощью запросов. Разберем детально, как это сделать правильно, используя разные подходы и учитывая нюансы производительности.

Способ 1. Использование вложенных таблиц в секции ВЫБРАТЬ

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

Рассмотрим пример запроса, который выбирает данные документа РеализацияТоваровУслуг вместе с двумя его табличными частями — Товары и Услуги:


ТекстЗапроса = "ВЫБРАТЬ
               |    РеализацияТоваровУслуг.Ссылка КАК Ссылка,
               |    РеализацияТоваровУслуг.Склад КАК Склад,
               |    РеализацияТоваровУслуг.Товары.(
               |        Номенклатура КАК Номенклатура,
               |        Количество КАК Количество,
               |        Цена КАК Цена
               |    ) КАК Товары,
               |    РеализацияТоваровУслуг.Услуги.(
               |        Номенклатура КАК Номенклатура,
               |        Количество КАК Количество,
               |        Цена КАК Цена
               |    ) КАК Услуги
               |ИЗ
               |    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг";

Проанализируем ситуацию: при таком подходе запрос возвращает иерархическую структуру. При обходе результата нам необходимо использовать метод Выбрать с параметром ОбходРезультатаЗапроса.ПоГруппировкам (хотя для вложенных таблиц это происходит автоматически при обращении к полю ТЧ). Посмотрим на программный код обработки такой выборки:


ВыборкаДокументы = Запрос.Выполнить().Выбрать();
Пока ВыборкаДокументы.Следующий() Цикл
    // Работаем с реквизитами шапки
    Сообщить("Документ: " + ВыборкаДокументы.Ссылка);

    // Выбираем данные из вложенной таблицы Товары
    ВыборкаТовары = ВыборкаДокументы.Товары.Выбрать();
    Пока ВыборкаТовары.Следующий() Цикл
        Сообщить("— Товар: " + ВыборкаТовары.Номенклатура + ", Кол-во: " + ВыборкаТовары.Количество);
    КонецЦикла;
КонецЦикла;

Преимущества этого способа: данные сгруппированы по документам, что очень удобно для создания новых объектов или вывода в сложные отчеты.

Способ 2. Обращение к физической таблице табличной части

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

Рассмотрим пример, где мы выбираем компоненты спецификации номенклатуры:


Запрос.Текст = "ВЫБРАТЬ
               |    СпецТЧ.Ссылка КАК ВладелецСпецификации,
               |    СпецТЧ.Ссылка.Наименование КАК НаименованиеСпецификации,
               |    СпецТЧ.Компонент,
               |    СпецТЧ.Количество
               |ИЗ
               |    Справочник.СпецификацииНоменклатуры.ТабличнаяЧасть КАК СпецТЧ";

Обратите внимание: через поле Ссылка мы можем получить доступ к любому реквизиту «шапки» документа или справочника (например, СпецТЧ.Ссылка.Дата). Выясним причину, почему этот способ иногда предпочтительнее: он позволяет накладывать фильтры непосредственно на строки табличной части (например, «найти все спецификации, где используется конкретный материал») гораздо быстрее, чем через вложенные таблицы.

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

Способ 3. Плоский запрос с итогами (Оптимальный для COM)

Если вы работаете через COM-соединение или планируете выгружать результат запроса в таблицу значений для последующей обработки, рекомендуется использовать конструкцию ИТОГИ ПО. Это позволяет избежать проблем с передачей вложенных объектов через границы программных модулей.


Запрос.Текст = "ВЫБРАТЬ
               |    Единицы.Ссылка КАК Ссылка,
               |    Единицы.Ссылка.Наименование КАК НоменклатураНаименование,
               |    Единицы.ЕдиницаИзмерения,
               |    Единицы.Коэффициент
               |ИЗ
               |    Справочник.Номенклатура.ЕдиницыИзмерения КАК Единицы
               |ИТОГИ ПО
               |    Ссылка";

Разберем по шагам процесс обработки этого запроса для заполнения нового объекта:

  1. Выполняем запрос и получаем выборку по группировкам: Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам).
  2. Во внешнем цикле создаем новый объект (например, новый справочник или документ).
  3. Во внутреннем цикле (детальные записи) добавляем строки в табличную часть нового объекта.

ВыборкаСсылка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаСсылка.Следующий() Цикл
    НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
    НовыйЭлемент.Наименование = ВыборкаСсылка.НоменклатураНаименование;

    ВыборкаДетальные = ВыборкаСсылка.Выбрать();
    Пока ВыборкаДетальные.Следующий() Цикл
        НоваяСтрока = НовыйЭлемент.ЕдиницыИзмерения.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, ВыборкаДетальные);
    КонецЦикла;

    НовыйЭлемент.Записать();
КонецЦикла;

Методы оптимизации: метод Загрузить()

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

  1. Выгружаем результат запроса (или его часть) в ТаблицуЗначений.
  2. Используем Объект.ИмяТабличнойЧасти.Загрузить(ТЗ).

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

Особенности работы через COM-соединение

При обращении к базе через COM-соединение (между двумя базами 1С) следует помнить, что передача сложных типов данных (таких как ВыборкаИзРезультатаЗапроса с вложенными таблицами) может привести к ошибкам или существенному падению скорости. Рассмотрим рекомендации для этого случая:

Индексация временных таблиц

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


// Помещаем данные ТЧ во временную таблицу для ускорения соединений
"ВЫБРАТЬ
|    Товары.Номенклатура,
|    Товары.Количество
|ПОМЕСТИТЬ ВТ_ДанныеТЧ
|ИЗ
|    &ТЧ КАК Товары
|ИНДЕКСИРОВАТЬ ПО
|    Номенклатура"

Индексация по полю связи (Номенклатура или Ссылка) позволяет СУБД мгновенно находить нужные строки, что критически важно для производительности системы при больших объемах данных.

← На главную