ПоместитьВоВременноеХранилище для таблицы значений: передача между клиентом, сервером и фоновым заданием

· 9 мин

Содержание

Таблицу значений помещают во временное хранилище одной строкой на сервере: Адрес = ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);. Назад её получают тоже на сервере: ТЗ = ПолучитьИзВременногоХранилища(Адрес);. На клиент уходит только строка-адрес, сама таблица остаётся на сервере.

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

Что такое временное хранилище 1С и когда в него кладут таблицу значений

Временное хранилище — это область данных сеанса на сервере 1С:Предприятия. Положили значение — получили строковый адрес вида e1cib/tempstorage/.... По этому адресу значение можно достать в другом вызове сервера, в другой форме или в фоновом задании.

Зачем это нужно именно для таблицы значений:

  • 📦 Передать таблицу между формами. Форма подбора собрала строки, форма документа должна их принять. Через параметры оповещения или закрытия передают не таблицу, а адрес.
  • ⏳ Вернуть результат фонового задания. Фоновое задание формирует большую таблицу, форма забирает её по адресу, когда задание завершилось.
  • 🔁 Не гонять данные туда-обратно. Таблицу на 50 000 строк не нужно хранить в реквизите формы и передавать с каждым серверным вызовом. Достаточно держать адрес.
  • 📄 Загрузка файлов. Excel или CSV загружают на сервер через хранилище, разбирают в таблицу значений и кладут результат по новому адресу.

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

Методы глобального контекста

МетодЧто делаетГде вызывать для таблицы значений
ПоместитьВоВременноеХранилище(Данные, Адрес)Кладёт значение, возвращает адресНа сервере
ПолучитьИзВременногоХранилища(Адрес)Возвращает значение или НеопределеноНа сервере
УдалитьИзВременногоХранилища(Адрес)Освобождает адресГде угодно
ЭтоАдресВременногоХранилища(Адрес)Проверяет, похожа ли строка на адрес хранилищаГде угодно

Готовим таблицу значений перед помещением во временное хранилище

Таблицу значений почти никогда не создают «с нуля» ради хранилища. Обычно её берут из запроса, табличной части или реквизита формы. Источник определяет, как получить объект ТаблицаЗначений.

Из запроса

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

Из табличной части объекта или реквизита формы

  • Табличная часть объекта на сервере: ТЗ = ДокументОбъект.Товары.Выгрузить();
  • Табличная часть в форме документа: ТЗ = Объект.Товары.Выгрузить(); в серверной процедуре формы.
  • Реквизит формы типа «Таблица значений»: ТЗ = РеквизитФормыВЗначение("ТаблицаДанных");

Обратная операция для реквизита формы — ЗначениеВРеквизитФормы(ТЗ, "ТаблицаДанных");.

Что проверить до помещения

  • ✂️ Лишние колонки. Если получателю нужны три колонки из пятнадцати, отрежьте остальные: ТЗ = ТЗ.Скопировать(, "Номенклатура, Количество, Цена");.
  • 🔢 Типы колонок. Колонку, созданную без описания типов, лучше типизировать: ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));. Так получатель не будет гадать, что лежит в ячейке.
  • 🧩 Сериализуемость. В клиент-серверном варианте хранилище живёт в данных сеанса, которые сервер сериализует. В ячейках держите ссылки, строки, числа, даты, ДвоичныеДанные. Объекты вроде Запрос или ЗаписьXML в таблицу для хранилища не кладите.

Способы поместить таблицу значений во временное хранилище

Разница между способами — во втором параметре. От него зависит, сколько проживёт значение.

Второй параметрСколько живёт значениеКогда применять
Не указанДо конца текущего обращения к серверуПочти никогда для таблиц, которые нужны позже
УникальныйИдентификатор формыПока открыта формаОсновной вариант для форм
Существующий адрес (строка)Сколько жил исходный адрес; значение заменяетсяПерезапись, результат фонового задания

Способ 1. С идентификатором формы

Самый надёжный вариант. Адрес хранят в реквизите формы типа «Строка», таблица удалится сама при закрытии формы.

&НаСервере
Процедура ЗаполнитьНаСервере()

	ТЗ = Объект.Товары.Выгрузить();
	АдресТаблицы = ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);

КонецПроцедуры

Здесь АдресТаблицы — реквизит формы, а УникальныйИдентификатор — свойство самой формы.

Способ 2. Из процедуры без контекста формы

В &НаСервереБезКонтекста свойства формы недоступны. Идентификатор передают параметром:

&НаКлиенте
Процедура Сформировать(Команда)

	АдресТаблицы = ПодготовитьТаблицу(УникальныйИдентификатор);

КонецПроцедуры

&НаСервереБезКонтекста
Функция ПодготовитьТаблицу(ИдентификаторФормы)

	Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Наименование ИЗ Справочник.Контрагенты");
	ТЗ = Запрос.Выполнить().Выгрузить();
	Возврат ПоместитьВоВременноеХранилище(ТЗ, ИдентификаторФормы);

КонецФункции

Серверный вызов без контекста дешевле: на сервер не уходят данные формы.

Способ 3. Перезапись по готовому адресу

Если адрес уже есть, вторым параметром передают его. Новое значение заменит старое, адрес не поменяется:

ПоместитьВоВременноеХранилище(ТЗ, АдресТаблицы);

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

Способ 4. Результат фонового задания

Адрес создают в форме заранее, с идентификатором формы. Фоновое задание кладёт результат по этому адресу:

// Модуль формы
&НаСервере
Функция ЗапуститьФормирование()

	АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
	Параметры = Новый Массив;
	Параметры.Добавить(АдресРезультата);
	ФоновыеЗадания.Выполнить("МойОбщийМодуль.СформироватьТаблицу", Параметры);
	Возврат АдресРезультата;

КонецФункции

// Общий модуль МойОбщийМодуль (серверный)
Процедура СформироватьТаблицу(АдресРезультата) Экспорт

	Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Наименование ИЗ Справочник.Номенклатура");
	ТЗ = Запрос.Выполнить().Выгрузить();
	ПоместитьВоВременноеХранилище(ТЗ, АдресРезультата);

КонецПроцедуры

Пока задание работает, ПолучитьИзВременногоХранилища(АдресРезультата) вернёт Неопределено. Таблица появится по адресу после завершения задания.

В типовых конфигурациях на БСП то же самое делает общий модуль ДлительныеОперации: функция ВыполнитьВФоне сама создаёт адрес результата и возвращает его в структуре. Если конфигурация на БСП, удобнее пользоваться им, а не писать запуск вручную.

Как получить таблицу значений из временного хранилища

Извлечение — одна строка, но с двумя проверками. Адрес мог устареть, а значение — оказаться не таблицей.

&НаСервереБезКонтекста
Функция КоличествоСтрок(Адрес)

	Если Не ЭтоАдресВременногоХранилища(Адрес) Тогда
		Возврат 0;
	КонецЕсли;

	Данные = ПолучитьИзВременногоХранилища(Адрес);
	Если ТипЗнч(Данные) <> Тип("ТаблицаЗначений") Тогда
		Возврат 0;
	КонецЕсли;

	Возврат Данные.Количество();

КонецФункции

Загрузка в табличную часть или реквизит формы

Типовая задача: форма подбора вернула адрес, документ должен добавить строки.

&НаСервере
Процедура ЗагрузитьПодобранное(Адрес)

	ТЗ = ПолучитьИзВременногоХранилища(Адрес);
	Если ТипЗнч(ТЗ) <> Тип("ТаблицаЗначений") Тогда
		Возврат;
	КонецЕсли;

	Для Каждого СтрокаТЗ Из ТЗ Цикл
		НоваяСтрока = Объект.Товары.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЗ);
	КонецЦикла;

	УдалитьИзВременногоХранилища(Адрес);

КонецПроцедуры

ЗаполнитьЗначенияСвойств переносит значения колонок с совпадающими именами. Колонки, которых нет в табличной части, просто пропускаются.

Если таблицу нужно показать целиком в реквизите формы типа «Таблица значений», хватит ЗначениеВРеквизитФормы(ТЗ, "ТаблицаДанных");. Набор колонок реквизита должен быть создан заранее в конфигураторе или программно.

Передача адреса между формами

Форма подбора возвращает адрес при закрытии: Закрыть(АдресТаблицы);. В форме документа обработчик оповещения получает этот адрес и вызывает серверную процедуру загрузки.

Одна тонкость. Если подбор поместил таблицу со своим УникальныйИдентификатор, при закрытии формы подбора значение удалится. Поэтому идентификатор принимающей формы передают в подбор параметром открытия, и подбор кладёт таблицу уже с ним.

Как работать с большими таблицами во временном хранилище

Хранилище держит данные в памяти рабочего процесса сервера. Таблица на сотни тысяч строк, помещённая десятью пользователями, ощутимо нагружает rphost.

Что реально помогает:

  1. Сужайте таблицу до помещения. Скопировать(, "Колонки") и Свернуть("Номенклатура", "Количество, Сумма") убирают лишние данные. Свёрнутая таблица по 2 000 позиций вместо 40 000 строк движений весит в разы меньше.
  2. Удаляйте после использования. УдалитьИзВременногоХранилища(Адрес) сразу после загрузки освобождает память, не дожидаясь закрытия формы.
  3. Не храните таблицу в реквизите формы параллельно. Либо реквизит, либо адрес. Иначе данные лежат дважды и ещё ездят на клиент.
  4. Сжимайте, если таблица живёт долго. Обёртка Новый ХранилищеЗначения(ТЗ, Новый СжатиеДанных(9)) даёт компактный объект. Его помещают в хранилище, а таблицу достают методом Получить().
  5. Не перечитывайте в цикле. Получили таблицу один раз в начале серверной процедуры — работайте с переменной. Каждый вызов ПолучитьИзВременногоХранилища заново отдаёт значение.

Сжатие экономит память, но тратит процессор на упаковку и распаковку. Для таблиц на сотни строк оно не нужно.

Поиск по большой таблице после получения

Если после извлечения нужно часто искать строки, добавьте индекс: ТЗ.Индексы.Добавить("Номенклатура");. Надёжнее создавать индексы уже после ПолучитьИзВременногоХранилища, прямо перед поиском.

Диагностика: выберите симптом

Ошибки при работе с временным хранилищем и как их избежать

Отдельно о трёх промахах, которые встречаются в чужом коде чаще других.

  • ❌ Адрес хранят в переменной модуля формы, а не в реквизите. Переменная модуля на сервере не сохраняется между вызовами. Через вызов адрес потерян. Храните его в реквизите формы типа «Строка».
  • ❌ Помещают на клиенте и ждут таблицу на сервере. На клиенте нельзя собрать таблицу значений, и поместить её оттуда нечего. Клиентский вызов ПоместитьВоВременноеХранилище уместен для структур и массивов простых значений, не для таблиц.
  • ❌ Путают временное хранилище с менеджером временных таблиц. МенеджерВременныхТаблиц работает внутри запросов и живёт в рамках одного серверного вызова, если его не сохранить. Таблицу значений из хранилища в запрос передают параметром: Запрос.УстановитьПараметр("Таблица", ТЗ); и ПОМЕСТИТЬ из &Таблица.

Пример: таблица из хранилища как источник запроса

ТЗ = ПолучитьИзВременногоХранилища(Адрес);

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

Колонки такой таблицы обязательно типизируйте. Запрос не примет колонку без описания типов или с типом «произвольный». Это частая причина ошибки на строке Запрос.Выполнить().

Чем заменить временное хранилище для таблицы значений

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

МеханизмСколько живётПодходит для
Реквизит формы типа «Таблица значений»Пока открыта формаНебольшие таблицы, которые видит пользователь
Параметры фонового заданияВремя выполнения заданияПередать таблицу в задание на входе
Реквизит типа ХранилищеЗначения в справочнике или регистреПостоянно, в базеНастройки, снимки данных между сеансами
Временный файл (ПолучитьИмяВременногоФайла)Пока файл не удалёнОчень большие выгрузки, обмен с внешними программами
ЗначениеВСтрокуВнутр и обратноСколько хранится строкаПередача через внешние каналы внутри 1С

Реквизит формы против адреса

Реквизит формы проще: не нужно думать про адреса и время жизни. Зато при каждом серверном вызове с контекстом данные реквизита передаются между клиентом и сервером. При 300 строках это незаметно, при 30 000 форма начинает тормозить на каждой кнопке.

Хранение между сеансами

Если таблица нужна завтра или другому пользователю, временное хранилище не подойдёт. Используйте реквизит с типом ХранилищеЗначения:

Запись.ДанныеТаблицы = Новый ХранилищеЗначения(ТЗ, Новый СжатиеДанных(9));
// чтение
ТЗ = Запись.ДанныеТаблицы.Получить();
Можно ли поместить во временное хранилище несколько таблиц сразу?
Да. Сложите их в структуру Новый Структура("Товары, Услуги", ТЗ1, ТЗ2) и поместите структуру. Извлекать её тоже нужно на сервере.
Доступно ли временное хранилище из другого сеанса?
Нет. Адрес действителен только в сеансе, где его создали, и в фоновых заданиях, запущенных из этого сеанса. Другой пользователь по тому же адресу ничего не получит.
Работает ли временное хранилище в файловой базе?
Да, методы те же и ведут себя так же. Правило «таблица значений только на сервере» в файловом варианте тоже действует, хотя клиент и сервер там один процесс.
Сколько строк можно положить во временное хранилище?
Жёсткого лимита на число строк нет. Ограничение — память рабочего процесса сервера. Таблицы в сотни тысяч строк лучше сужать, сворачивать или выгружать во временный файл.
Чем адрес хранилища отличается от навигационной ссылки?
Адрес хранилища начинается с e1cib/tempstorage/ и временный. Навигационная ссылка указывает на объект базы и постоянна. Для проверки адреса используйте ЭтоАдресВременногоХранилища.