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

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

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

Разбор первой ситуации: Почему в консоли данные есть, а в коде — нет?

Рассмотрим типичный сценарий. Разработчик пишет запрос к основной таблице документа, обращаясь к его табличной части через точку. Проанализируем пример такого кода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПеремещениеТоваров.Дата КАК Дата,
    |	ПеремещениеТоваров.Товары.Номенклатура КАК Номенклатура
    |ИЗ
    |	Документ.ПеремещениеТоваров КАК ПеремещениеТоваров
    |ГДЕ
    |	ПеремещениеТоваров.Ссылка В(&МассивОбъектов)";

Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

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

Выясним причину такого поведения. Когда в тексте запроса мы указываем путь ПеремещениеТоваров.Товары.Номенклатура, система 1С воспринимает поле Товары как вложенную таблицу. В консоли запросов предусмотрены специальные алгоритмы визуализации, которые автоматически "разворачивают" такие таблицы для удобства пользователя. Однако в программном коде объект ВыборкаИзРезультатаЗапроса работает иначе.

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


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

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

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

Решение №2: Прямой запрос к таблице табличной части

Опытные разработчики рекомендуют обращаться не к таблице документа, а напрямую к таблице его табличной части. В платформе 1С каждая табличная часть — это отдельная физическая таблица в базе данных SQL. Она содержит не только свои реквизиты, но и обязательное поле Ссылка, которое указывает на родительский документ.

Разберем пример запроса, который сразу возвращает "плоский" результат без вложенных таблиц:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Товары.Ссылка.Дата КАК Дата,
    |	Товары.Номенклатура КАК Номенклатура
    |ИЗ
    |	Документ.ПеремещениеТоваров.Товары КАК Товары
    |ГДЕ
    |	Товары.Ссылка В(&МассивОбъектов)";

Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

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

Преимущества этого метода:

  1. Простота кода: нам нужен всего один цикл Пока ... Цикл для обработки всех строк всех документов.
  2. Производительность: системе проще оптимизировать запрос к одной физической таблице.
  3. Гибкость: через поле Ссылка мы можем получить любой реквизит "шапки" документа (дату, номер, склад, контрагента).

Проблема "исчезающих" документов и пустых табличных частей

Проанализируем ситуацию, упомянутую в обсуждении: что если в документе пустая табличная часть? Если мы делаем запрос напрямую к Документ.ПеремещениеТоваров.Товары, то документы без строк вообще не попадут в результат запроса. Для SQL это эквивалентно операции INNER JOIN.

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


Запрос.Текст = 
    "ВЫБРАТЬ
    |	Перемещение.Ссылка КАК ДокументСсылка,
    |	Перемещение.Дата КАК Дата,
    |	ЕСТЬNULL(Товары.Номенклатура, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Номенклатура
    |ИЗ
    |	Документ.ПеремещениеТоваров КАК Перемещение
    |		ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПеремещениеТоваров.Товары КАК Товары
    |		ПО Перемещение.Ссылка = Товары.Ссылка
    |ГДЕ
    |	Перемещение.Ссылка В(&МассивОбъектов)";

В данном случае мы берем таблицу документов как основную и "приклеиваем" к ней строки товаров. Если строк нет, поле Номенклатура примет значение NULL, которое мы корректно обрабатываем функцией ЕСТЬNULL.

Работа с типизацией и методом Выгрузить()

Часто для удобства обработки данных программисты используют метод РезультатЗапроса.Выгрузить(). Рассмотрим, как ведет себя система в зависимости от структуры запроса:

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

Итоги и рекомендации

Подводя итог нашему разбору, выделим основные правила работы с табличными частями в 1С:

  1. Не доверяйте слепо Консоли запросов. Всегда помните, что визуальное представление данных в консоли может отличаться от программной структуры РезультатЗапроса.
  2. Используйте вложенные циклы, если ваш запрос построен от "шапки" документа и содержит поля табличных частей через точку.
  3. Отдавайте предпочтение прямым запросам к таблицам табличных частей (Документ.Имя.ИмяТЧ), если вам нужна плоская структура данных.
  4. Помните о ссылочной целостности. Поле Ссылка в табличной части — это мощный инструмент, позволяющий достать любые данные документа-хозяина.
  5. Контролируйте пустые значения. При использовании соединений всегда применяйте функцию ЕСТЬNULL, чтобы избежать ошибок при дальнейшей обработке данных в коде.

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

← На главную