При разработке сложных механизмов проверки или расчета данных в 1С перед программистом часто встает дилемма: перебирать строки табличной части (ТЧ) документа в цикле или использовать мощь языка запросов. Использование запросов кажется более элегантным и удобным, особенно когда нужно сопоставить данные ТЧ с информацией из регистров или справочников. Однако здесь скрываются нюансы производительности, связанные с передачей данных между сервером приложений и СУБД. Разберем подробно, как правильно работать с данными объекта в запросе, какие существуют методы и как избежать типичных ошибок замедления системы.
Это самый распространенный метод, когда данные документа еще не записаны в базу данных или были изменены пользователем в экранной форме. Мы можем передать табличную часть непосредственно в запрос. Система 1С автоматически преобразует ее в таблицу значений и создаст временную таблицу на стороне SQL-сервера.
Рассмотрим пример реализации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВременнаяТаблицаТЧ.Номенклатура КАК Номенклатура,
| ВременнаяТаблицаТЧ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_ДанныеОбъекта
|ИЗ
| &ТЧ КАК ВременнаяТаблицаТЧ
|;
|
|ВЫБРАТЬ
| ВТ_ДанныеОбъекта.Номенклатура,
| ВТ_ДанныеОбъекта.Количество,
| ЦеныНоменклатурыСрезПоследних.Цена
|ИЗ
| ВТ_ДанныеОбъекта КАК ВТ_ДанныеОбъекта
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК ЦеныНоменклатурыСрезПоследних
| ПО ВТ_ДанныеОбъекта.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура";
Запрос.УстановитьПараметр("ТЧ", Объект.Товары.Выгрузить());
Запрос.УстановитьПараметр("Период", ТекущаяДата());
РезультатЗапроса = Запрос.Выполнить();
Проанализируем этот подход. Метод Выгрузить() работает достаточно быстро, но основная нагрузка ложится на создание временной таблицы в базе tempdb на SQL-сервере. Чтобы минимизировать издержки, всегда выгружайте только те колонки, которые действительно необходимы для расчетов. Это значительно сократит объем передаваемых данных.
// Оптимизированная выгрузка только нужных колонок
СписокКолонок = "Номенклатура, Количество, Склад";
ТаблицаДляЗапроса = Объект.Товары.Выгрузить( , СписокКолонок);
Запрос.УстановитьПараметр("ТЧ", ТаблицаДляЗапроса);
Если документ уже записан, наиболее эффективным способом будет обращение к табличной части документа через метаданные. В этом случае данные не пересылаются с сервера приложений на SQL, так как они уже находятся в базе данных.
Рассмотрим, как это выглядит в коде:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДокТовары.Номенклатура,
| ДокТовары.Количество
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК ДокТовары
|ГДЕ
| ДокТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
Важный нюанс: этот метод не подходит для проверки данных в процессе заполнения документа до его записи. Если пользователь изменил количество в строке, но не нажал «Записать», запрос к физической таблице вернет старые данные. Поэтому для оперативных проверок при изменении данных формы используется Способ 1.
Многие начинающие программисты считают, что запросы — это всегда быстрее. Однако для небольших табличных частей (например, до 50-100 строк) обычный цикл Для Каждого может оказаться производительнее. Это связано с тем, что на инициализацию объекта Запрос, парсинг текста запроса и создание временных таблиц в SQL уходят ресурсы, которые могут превышать время простого перебора в памяти сервера приложений.
Однако, как только внутри цикла появляется «обращение через точку» (например, СтрокаТЧ.Номенклатура.Артикул), производительность резко падает. Каждое такое обращение генерирует отдельный неявный запрос к базе данных. В профессиональной среде это называют «быдлокодом» или проблемой N+1 запроса. Если вам нужны дополнительные данные из справочников или регистров, всегда используйте запросы с предварительной загрузкой ТЧ во временную таблицу.
Если вы решили использовать запрос для работы с данными объекта, обратите внимание на МенеджерВременныхТаблиц. Он позволяет не просто передать ТЗ в параметр, а создать структуру с индексами, что критически важно для производительности соединений (JOIN).
Выясним, как правильно индексировать данные ТЧ в запросе:
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Шаг 1: Помещаем ТЧ во временную таблицу и индексируем ключевые поля
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеТЧ.Номенклатура КАК Номенклатура,
| ДанныеТЧ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| &ТЗ КАК ДанныеТЧ
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура";
Запрос.УстановитьПараметр("ТЗ", Объект.Товары.Выгрузить(,"Номенклатура,Количество"));
Запрос.Выполнить();
// Шаг 2: Работаем с проиндексированной таблицей в последующих запросах пакета
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Товары.Номенклатура,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
|ИЗ
| ВТ_Товары КАК ВТ_Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК Остатки
| ПО ВТ_Товары.Номенклатура = Остатки.Номенклатура";
Использование ИНДЕКСИРОВАТЬ ПО позволяет СУБД мгновенно находить соответствия при соединении таблиц, что особенно заметно на документах с сотнями и тысячами строк.
Проанализируем еще один аспект: типизацию. Когда вы передаете ТаблицуЗначений в запрос, 1С пытается автоматически определить типы колонок. Если колонка в ТЗ не имеет явно заданного типа (тип Произвольный), 1С может создать на SQL-сервере очень «широкое» поле (например, NVARCHAR(MAX)), что замедлит работу. Перед передачей ТЗ в запрос желательно убедиться, что колонки имеют четкую типизацию, соответствующую типам метаданных.
Для Каждого. Это быстрее и проще в отладке.ТаблицуЗначений, передавайте ее в запрос как параметр, обязательно используйте временные таблицы и индексацию.Документ.ИмяДокумента.Товары по ссылке. Это самый производительный вариант для СУБД.ТаблицуЗначений, передайте ее в один пакетный запрос и обработайте результат целиком.Таким образом, работа с данными объекта в запросе — это мощный инструмент, требующий понимания того, как данные мигрируют между оперативной памятью и дисковым пространством SQL-сервера. Соблюдение правил индексации и ограничение состава колонок при выгрузке позволит вашим алгоритмам работать максимально быстро даже на больших объемах данных.