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

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

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

Разбор ситуации и типичных ошибок

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


РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выгрузить(); // Получаем таблицу значений
Для каждого НоваяСтрока Из Объект.Товары Цикл
    // ОШИБКА: Попытка искать по коду, передавая в параметр всю таблицу
    ОбъектВыборки = Справочники.НомераГТД.НайтиПоКоду(Выборка); 
    НоваяСтрока.НомерГТД = ОбъектВыборки;
КонецЦикла;

Почему этот код не работает (выявить такие огрехи поможет статический анализатор кода):

  1. Метод НайтиПоКоду() ожидает строку или число (код), а не объект типа ТаблицаЗначений.
  2. Метод Выгрузить() создает копию данных в памяти, но не предоставляет механизма автоматического поиска нужной строки для каждой строки табличной части.
  3. Если запрос вернул ссылку, то повторно искать её методом НайтиПоКоду() бессмысленно и вредно для производительности.

Способ 1. Использование объекта Соответствие (Map)

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

Рассмотрим пошагово этот алгоритм (отлаживать текст запроса удобнее всего через управляемую консоль запросов):


// 1. Подготавливаем список номенклатуры из ТЧ для фильтра в запросе
СписокНоменклатуры = Объект.Товары.ВыгрузитьКолонку("Номенклатура");

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

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

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

// 3. Заполняем табличную часть
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    НайденныйГТД = СоответствиеГТД.Получить(СтрокаТЧ.Номенклатура);
    Если НайденныйГТД <> Неопределено Тогда
        СтрокаТЧ.НомерГТД = НайденныйГТД;
    КонецЕсли;
КонецЦикла;

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

Способ 2. Построчный обход выборки (Добавление новых строк)

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

Выборка из результата запроса — это своего рода "курсор". Пока вы не вызвали метод Следующий(), она не указывает ни на какую строку. Как только Следующий() возвращает Истина, текущие данные строки становятся доступны.


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

Объект.Товары.Очистить(); // Очищаем старые данные

Пока Выборка.Следующий() Цикл
    НоваяСтрока = Объект.Товары.Добавить();
    // Используем ЗаполнитьЗначенияСвойств для копирования всех совпадающих по именам колонок
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
    
    // Либо заполняем вручную, если имена колонок в запросе и ТЧ разные
    // НоваяСтрока.НомерГТД = Выборка.НомерГТДБезРНПТ;
КонецЦикла;

Способ 3. Прямая загрузка таблицы (Метод Загрузить)

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


// В запросе обязательно используем псевдонимы (КАК), совпадающие с именами реквизитов ТЧ
Запрос.Текст = "ВЫБРАТЬ Номенклатура, Количество, НомерГТД ИЗ ...";
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();

// ВНИМАНИЕ: Это полностью заменит все строки в ТЧ новыми данными из таблицы
Объект.Товары.Загрузить(ТаблицаРезультата);

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

Важные рекомендации по работе с типами данных

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

Если в вашем запросе получено поле НомерГТД, и это ссылка на справочник, то не нужно использовать НайтиПоКоду(). Вы уже держите в руках готовый объект. Просто присвойте его: СтрокаТЧ.НомерГТД = Выборка.НомерГТД;.

Если же вы получили из внешнего источника строковый номер (например, "10113080/010123/0005555") и хотите найти его в базе:

  1. Сначала проверьте, нет ли такого номера уже в вашей выборке запроса (через левое соединение со справочником в самом запросе). Это самый быстрый путь.
  2. Если приходится искать в цикле, используйте Справочники.НомераГТД.НайтиПоНаименованию(СтрокаИзЗапроса, Истина) или НайтиПоКоду, но помните, что поиск в цикле — это удар по производительности.

Метод с использованием временных таблиц (Advanced)

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


// Выгружаем ТЧ во временную таблицу для запроса
ТаблицаТоваров = Объект.Товары.Выгрузить();
Запрос.УстановитьПараметр("ВТ_Товары", ТаблицаТоваров);

Запрос.Текст = 
    "ВЫБРАТЬ * ПОМЕСТИТЬ ТЧ_Товары ИЗ &ВТ_Товары КАК ВТ_Товары;
    |////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ТЧ.НомерСтроки,
    |	ТоварыОрганизаций.НомерГТД
    |ИЗ
    |	ТЧ_Товары КАК ТЧ
    |ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций КАК ТоварыОрганизаций
    |	ПО ТЧ.Номенклатура = ТоварыОрганизаций.Номенклатура";

// После выполнения такого запроса мы получаем таблицу, 
// где четко указано, в какую строку ТЧ (по индексу НомерСтроки) какой ГТД записать.

Используя НомерСтроки, мы можем обращаться напрямую к нужной строке табличной части: Объект.Товары[СтрокаРезультата.НомерСтроки - 1].НомерГТД = СтрокаРезультата.НомерГТД;. Это исключает любые ошибки поиска и сопоставления.

← На главную