Вопрос выбора между использованием Таблицы Значений (ТЗ) и Временной Таблицы (ВТ) в среде 1С:Предприятие является фундаментальным для оптимизации производительности системы. На первый взгляд может показаться, что работа с данными в оперативной памяти (ТЗ) всегда быстрее, чем обращение к СУБД. Однако, как показывает практика и детальный разбор механизмов платформы, ответ зависит от множества факторов: объема данных, сложности алгоритма и накладных расходов на передачу информации между сервером приложений и сервером баз данных.
В этой статье мы подробно проанализируем, в каких ситуациях стоит отдать предпочтение конкретному инструменту, разберем технические нюансы работы MS SQL и памяти сервера 1С, а также рассмотрим практические примеры кода.
Когда мы работаем с объектом ТаблицаЗначений на стороне сервера приложений, данные располагаются в оперативной памяти рабочего процесса rphost. Рассмотрим особенности этого подхода:
ТаблицыЗначений построены индексы (с помощью метода Индексы.Добавить()), поиск по ней происходит чрезвычайно быстро. Платформа использует хэш-таблицы, что обеспечивает практически мгновенный доступ к нужной строке по ключу.ТаблицаЗначений может разрастись до нескольких гигабайт, что способно вызвать деградацию всей системы или аварийное завершение процесса rphost.Временные таблицы создаются в базе данных tempdb на стороне SQL-сервера. Рассмотрим, что происходит "под капотом", когда мы помещаем ТЗ во временную таблицу:
Проанализируем процесс маршалинга: при выполнении запроса на помещение данных из ТЗ во временную таблицу, сервер приложений 1С формирует пакет данных (часто в виде инструкций INSERT INTO или аналогичных механизмов быстрой загрузки) и передает их SQL-серверу. Это само по себе является затратной операцией по времени.
Однако у ВТ есть неоспоримые преимущества:
Выясним причину, почему программисты часто спорят о "запросах в цикле" применительно к временным таблицам. Разберем ситуацию на примере получения цен для списка товаров.
Рассмотрим пример кода, где поиск осуществляется запросом к временной таблице внутри цикла (традиционно плохая практика):
// Предположим, ВТ_ТЗ уже создана и наполнена данными
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МойМенеджерВТ;
Для Каждого Стр Из Товары Цикл
Запрос.Текст = "ВЫБРАТЬ Цена ИЗ ВТ_ТЗ ГДЕ Номенклатура = &Номенклатура";
Запрос.УстановитьПараметр("Номенклатура", Стр.Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Стр.Цена = Выборка.Цена;
КонецЕсли;
КонецЦикла;
Почему это медленно? Несмотря на то, что данные лежат во временной таблице, каждый вызов Запрос.Выполнить() заставляет сервер приложений 1С инициировать сеанс связи с SQL-сервером. Накладные расходы на саму инициализацию запроса (parse, compile, execute) могут превышать время поиска данных в сотни раз. В 1С отсутствует полноценный механизм Prepare/Execute для таких случаев, поэтому каждое выполнение — это "тяжелая" операция.
Как сделать правильно? Вместо цикла по ВТ следует выполнить одно соединение временной таблицы с основной таблицей данных или просто выгрузить результат в индексированную ТЗ один раз.
Проанализируем примерные пороги эффективности, выведенные на основе практического опыта разработчиков:
tempdb справляется с такими объемами стабильнее.ADO или пакетные запросы).Разберем по шагам, как эффективно решить задачу сопоставления данных из внешнего источника (например, Excel) с данными в базе 1С, используя синергию ТЗ и запросов.
Сначала мы загружаем данные в ТаблицуЗначений. Обязательно типизируем колонки, чтобы избежать проблем при передаче в запрос.
Вместо того чтобы искать данные по одному, мы передаем всю таблицу в запрос целиком.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Передаем ТЗ как параметр
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Номенклатура КАК Номенклатура,
| ТЗ.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ВнешняяТЗ КАК ТЗ;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Данные.Номенклатура,
| ВТ_Данные.Количество,
| Цены.Цена
|ИЗ
| ВТ_Данные КАК ВТ_Данные
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены
| ПО ВТ_Данные.Номенклатура = Цены.Номенклатура";
Запрос.УстановитьПараметр("ВнешняяТЗ", МояТаблицаЗначений);
Запрос.УстановитьПараметр("Период", ТекущаяДата());
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
Что мы получили? Мы совершили всего ОДНО обращение к СУБД. Платформа 1С сама сформировала оптимальный способ передачи ТЗ на сервер SQL, а SQL-сервер выполнил соединение с физической таблицей цен максимально эффективно. Это в десятки раз быстрее, чем перебор ТЗ в цикле с выполнением запросов к ценам для каждой строки.
Проанализировав ситуацию, можно сделать следующие выводы:
Помните, что индексация в ТЗ (Таблица.Индексы.Добавить("Колонка")) так же важна, как и индексация временных таблиц в запросе (ИНДЕКСИРОВАТЬ ПО...). Правильное использование этих инструментов позволит вашей системе работать стабильно даже при значительных нагрузках.