Как эффективно работать с данными табличных частей документа в запросах

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

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

Способ 1. Использование табличной части как параметра запроса

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

Рассмотрим пример реализации:


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

Запрос.УстановитьПараметр("ТЧ", Объект.Товары.Выгрузить());
Запрос.УстановитьПараметр("Период", ТекущаяДата());

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

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


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

Способ 2. Запрос к физическим таблицам базы данных

Если документ уже записан, наиболее эффективным способом будет обращение к табличной части документа через метаданные. В этом случае данные не пересылаются с сервера приложений на SQL, так как они уже находятся в базе данных.

Рассмотрим, как это выглядит в коде:


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

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

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

Способ 3. Построчный обход (Цикл)

Многие начинающие программисты считают, что запросы — это всегда быстрее. Однако для небольших табличных частей (например, до 50-100 строк) обычный цикл Для Каждого может оказаться производительнее. Это связано с тем, что на инициализацию объекта Запрос, парсинг текста запроса и создание временных таблиц в SQL уходят ресурсы, которые могут превышать время простого перебора в памяти сервера приложений.

Однако, как только внутри цикла появляется «обращение через точку» (например, СтрокаТЧ.Номенклатура.Артикул), производительность резко падает. Каждое такое обращение генерирует отдельный неявный запрос к базе данных. В профессиональной среде это называют «быдлокодом» или проблемой N+1 запроса. Если вам нужны дополнительные данные из справочников или регистров, всегда используйте запросы с предварительной загрузкой ТЧ во временную таблицу.

Оптимизация работы с временными таблицами

Если вы решили использовать запрос для работы с данными объекта, обратите внимание на МенеджерВременныхТаблиц. Он позволяет не просто передать ТЗ в параметр, а создать структуру с индексами, что критически важно для производительности соединений (JOIN).

Выясним, как правильно индексировать данные ТЧ в запросе:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

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

Запрос.УстановитьПараметр("ТЗ", Объект.Товары.Выгрузить(,"Номенклатура,Количество"));
Запрос.Выполнить();

// Шаг 2: Работаем с проиндексированной таблицей в последующих запросах пакета
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ВТ_Товары.Номенклатура,
    |    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
    |ИЗ
    |    ВТ_Товары КАК ВТ_Товары
    |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК Остатки
    |        ПО ВТ_Товары.Номенклатура = Остатки.Номенклатура";

Использование ИНДЕКСИРОВАТЬ ПО позволяет СУБД мгновенно находить соответствия при соединении таблиц, что особенно заметно на документах с сотнями и тысячами строк.

Типизация колонок таблицы значений

Проанализируем еще один аспект: типизацию. Когда вы передаете ТаблицуЗначений в запрос, 1С пытается автоматически определить типы колонок. Если колонка в ТЗ не имеет явно заданного типа (тип Произвольный), 1С может создать на SQL-сервере очень «широкое» поле (например, NVARCHAR(MAX)), что замедлит работу. Перед передачей ТЗ в запрос желательно убедиться, что колонки имеют четкую типизацию, соответствующую типам метаданных.

Когда и какой метод выбирать?

  1. Малые ТЧ (до 50 строк) без связей с другими таблицами: Используйте простой цикл Для Каждого. Это быстрее и проще в отладке.
  2. Данные не записаны, нужны проверки по регистрам: Выгружайте ТЧ в ТаблицуЗначений, передавайте ее в запрос как параметр, обязательно используйте временные таблицы и индексацию.
  3. Данные записаны, нужны сложные расчеты: Делайте прямой запрос к таблицам Документ.ИмяДокумента.Товары по ссылке. Это самый производительный вариант для СУБД.
  4. Обработка множества документов: Никогда не делайте запрос в цикле. Сначала соберите все данные ТЧ всех документов в одну большую ТаблицуЗначений, передайте ее в один пакетный запрос и обработайте результат целиком.

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

← На главную