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

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

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

Анализ исходной ситуации и разбор типичных ошибок

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


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

В этом небольшом фрагменте кода сосредоточено сразу несколько критических архитектурных ошибок, которые не позволят программе работать корректно или сделают её работу крайне медленной. Выясним причины этих ошибок и разберем их подробно.

Ошибка первая: Запрос внутри цикла (Anti-pattern)

Запуск нового запроса к базе данных на каждой итерации цикла — это одна из самых грубых ошибок в программировании на платформе 1С. Почему так делать нельзя? Каждое выполнение метода Выполнить() генерирует полноценное обращение к СУБД (например, MS SQL Server или PostgreSQL). Если ваша табличная часть содержит 1000 строк, система совершит 1000 отдельных запросов по сети. Это приведет к колоссальной нагрузке на сервер, взаимным блокировкам таблиц и зависанию интерфейса пользователя. На реальных базах данных такой код просто парализует работу системы, а на экзамене «1С:Специалист» за подобное решение вы гарантированно получите неудовлетворительную оценку.

Ошибка вторая: Несоответствие типов данных в параметрах

Автор пытается установить строковый параметр: Запрос.УстановитьПараметр("ТипТелефона", "Обычный"). Однако реквизит ТипТелефона в справочнике Абоненты, скорее всего, имеет тип ссылки (например, СправочникСсылка.ТипыТелефонов), а не простой текстовой строки. Для СУБД ссылка и строка — это абсолютно разные сущности. Попытка сравнить их напрямую приведет к тому, что запрос всегда будет возвращать пустой результат. Платформа не сможет сопоставить уникальный идентификатор (GUID) ссылки с текстовым литералом.

Ошибка третья: Отсутствие механизмов записи данных в ТЧ

Автор выгрузил табличную часть во временную таблицу значений ТабЧасть = Объект.Абоненты.Выгрузить(), обошел её циклом, получил выборку из запроса, но так и не связал эти данные с исходным объектом. Изменения во временной таблице значений никак автоматически не переносятся обратно в реальную табличную часть объекта, если не вызвать соответствующий метод.

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

Способ 1. Заполнение ТЧ через обход выборки и метод Добавить()

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

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


// 1. Создаем запрос и описываем его текст
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Абоненты.Ссылка КАК ФИО
    |ИЗ
    |    Справочник.Абоненты КАК Абоненты
    |ГДЕ
    |    Абоненты.ТипТелефона = &ТипТелефона";

// 2. Правильно определяем параметр запроса (ищем ссылку по наименованию)
СсылкаТипТелефона = Справочники.ТипыТелефонов.НайтиПоНаименованию("Обычный");
Запрос.УстановитьПараметр("ТипТелефона", СсылкаТипТелефона);

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

// 4. Обходим выборку и добавляем строки в табличную часть
Пока Выборка.Следующий() Цикл
    // Создаем новую строку в табличной части "Абоненты" нашего объекта
    НоваяСтрока = Объект.Абоненты.Добавить();
    
    // Заполняем реквизит "ФИО" в новой строке значением из выборки запроса
    НоваяСтрока.ФИО = Выборка.ФИО;
КонецЦикла;

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

Способ 2. Быстрое заполнение с помощью метода Загрузить() (Рекомендуемый способ)

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

Этот метод работает на уровне платформы крайне быстро. Чтобы воспользоваться им, необходимо соблюсти одно важное правило: имена колонок во временной таблице значений, получаемой из запроса, должны полностью совпадать с именами реквизитов вашей табличной части. В нашем случае реквизит табличной части называется ФИО, поэтому в запросе мы должны переименовать поле Ссылка с помощью конструкции КАК ФИО.

Рассмотрим, как выглядит этот лаконичный и быстрый код:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Абоненты.Ссылка КАК ФИО
    |ИЗ
    |    Справочник.Абоненты КАК Абоненты
    |ГДЕ
    |    Абоненты.ТипТелефона = &ТипТелефона";

// Устанавливаем параметр-ссылку
Запрос.УстановитьПараметр("ТипТелефона", Справочники.ТипыТелефонов.НайтиПоНаименованию("Обычный"));

// Выполняем запрос и сразу выгружаем его результат в Таблицу Значений
ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();

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

Этот способ является стандартом разработки в 1С при полной перезаписи табличных частей. Он минимизирует количество строк кода и работает значительно быстрее ручного перебора.

Способ 3. Оптимизация добавления через ЗаполнитьЗначенияСвойств()

Что делать, если вам все-таки нужно добавлять строки в цикле (например, по пути требуется выполнять сложные дополнительные проверки или расчеты), но колонок в табличной части много, и писать для каждой строки конструкции вида НоваяСтрока.Реквизит1 = Выборка.Реквизит1 слишком долго? В таких случаях на помощь приходит универсальная функция 1С ЗаполнитьЗначенияСвойств().

Эта функция автоматически сопоставляет свойства источника и приемника по их именам и копирует значения только в совпадающие реквизиты. Посмотрим на практический пример ее использования:


Запрос = Новый Запрос;
// Выбираем несколько полей, имена которых совпадают с реквизитами ТЧ
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Абоненты.Ссылка КАК ФИО,
    |    Абоненты.НомерТелефона КАК НомерТелефона,
    |    Абоненты.Адрес КАК Адрес
    |ИЗ
    |    Справочник.Абоненты КАК Абоненты
    |ГДЕ
    |    Абоненты.ТипТелефона = &ТипТелефона";

Запрос.УстановитьПараметр("ТипТелефона", Справочники.ТипыТелефонов.НайтиПоНаименованию("Обычный"));

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

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

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

Важная деталь: Оптимальная работа со ссылочными параметрами запроса

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

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

  1. Предопределенные элементы: Если тип телефона «Обычный» создан в конфигураторе как предопределенное значение, к нему можно обратиться напрямую в коде как Справочники.ТипыТелефонов.Обычный или использовать конструкцию ЗНАЧЕНИЕ(Справочник.ТипыТелефонов.Обычный) непосредственно внутри текста запроса, полностью отказавшись от передачи этого параметра через УстановитьПараметр().
  2. Использование констант или настроек: Хранить ссылку на нужный тип телефона в специальной константе или регистре сведений «Настройки программы», откуда считывать её перед выполнением запроса.

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

← На главную