Как правильно сохранить табличные данные между сессиями в 1С: табличная часть справочника против регистра сведений и хранилища значений

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

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

В этой статье мы подробно разберем ситуацию, когда разработчик пытается использовать табличную часть (ТЧ) единственного элемента справочника или документа в качестве «хранилища» для временной таблицы, чтобы избежать работы с регистрами сведений. Мы проанализируем, почему такой подход является ошибочным, какие скрытые угрозы он в себе таит, и рассмотрим правильные, производительные альтернативы с готовыми примерами кода.


В чем суть проблемы и почему возникает соблазн использовать ТЧ?

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

Работать с независимыми регистрами сведений многим кажется более трудоемким процессом: необходимо создавать наборы записей, настраивать отборы, управлять менеджерами записи. Из-за этого возникает соблазн создать в конфигурации специальный справочник (или документ) со служебной табличной частью, программно создать в нем один-единственный элемент и использовать его табличную часть для записи, чтения и очистки данных. На первый взгляд код выглядит просто, но этот подход имеет критические недостатки.


Критические недостатки использования ТЧ единственного элемента

Рассмотрим подробнее, почему использование табличной части одного элемента справочника для постоянной перезаписи данных — это плохая практика:

  1. Жесткое ограничение на количество строк. В платформе 1С (особенно в старых версиях, таких как 1С 8.2, и вплоть до 8.3.25) длина номера строки табличной части жестко ограничена 5 знаками. Это означает, что вы физически не сможете записать в ТЧ более 99 999 строк. Если ваши данные превысят этот лимит, система выдаст ошибку транзакции, и данные будут потеряны. Для независимых регистров сведений такого ограничения не существует.
  2. Объектные и транзакционные блокировки. Даже если база данных является локальной или в ней работает мало пользователей, использование одного элемента справочника парализует параллельную работу. Как только один сеанс начнет изменять или перезаписывать данные в этой ТЧ, платформа наложит пессимистическую блокировку на данный элемент справочника. Другой пользователь или фоновое задание при попытке обратиться к этому же элементу получат ошибку «Объект заблокирован».
  3. Огромная нагрузка на оперативную память (RAM). Объектная модель 1С устроена так, что при обращении к справочнику через метод ПолучитьОбъект() платформа считывает элемент из базы данных в память сервера полностью — со всеми его реквизитами и всеми строками табличной части. Если в вашей таблице накопилось 50 000 строк, то при каждом чтении или записи система будет гонять этот массив данных между СУБД, сервером приложений и клиентом. Это приведет к зависаниям интерфейса и перерасходу памяти. В то же время регистры сведений позволяют считывать и записывать данные порционно с помощью запросов.
  4. Неэффективная перезапись в СУБД. При вызове метода Записать() для объекта справочника СУБД выполняет ресурсоемкую операцию: она полностью удаляет все старые строки табличной части этого элемента в таблице базы данных, а затем строчно вставляет новые. Это порождает колоссальную нагрузку на дисковую подсистему и базу данных.

Решение №1: Правильная и простая работа с независимым регистром сведений

Выясним причину, почему разработчики боятся регистров сведений. Основной аргумент — «сложный» код для очистки и перезаписи данных. На самом деле это миф. Давайте посмотрим на реальные примеры кода для работы с независимым регистром сведений (без измерений или с минимальным набором измерений).

Для полной очистки таблицы регистра сведений не нужно перебирать записи в цикле. Достаточно записать пустой набор записей. Это делается буквально в одну строку:


// Полная очистка независимого регистра сведений
РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей().Записать();

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


// 1. Создаем набор записей регистра
НаборЗаписей = РегистрыСведений.ИмяВашегоРегистра.СоздатьНаборЗаписей();

// 2. Загружаем таблицу значений (структура колонок ТЗ должна совпадать с полями регистра)
НаборЗаписей.Загрузить(ВашаТаблицаЗначений);

// 3. Записываем набор в базу данных с заменой существующих записей
НаборЗаписей.Записать(Истина);

Этот код работает в разы быстрее, чем перезапись табличной части справочника, не вызывает объектных блокировок и не имеет лимита в 99 999 строк.


Решение №2: Использование объектных методов через модуль менеджера регистра

Если вам все же очень хочется работать с регистром сведений в «объектном» стиле (используя привычные методы Очистить(), Загрузить() и Выгрузить()), вы можете один раз написать программный интерфейс в модуле менеджера вашего регистра сведений. Это красивое и профессиональное решение.

Откройте модуль менеджера регистра сведений ИмяВашегоРегистра и добавьте в него следующие экспортные процедуры и функции:


// Процедура полной очистки регистра
Процедура Очистить() Экспорт
    Набор = СоздатьНаборЗаписей();
    Набор.Записать(Истина);
КонецПроцедуры

// Функция выгрузки всех данных регистра в Таблицу Значений
Функция Выгрузить() Экспорт
    Набор = СоздатьНаборЗаписей();
    Набор.Прочитать();
    Возврат Набор.Выгрузить();
КонецФункции

// Процедура загрузки данных из Таблицы Значений с полной заменой
Процедура Загрузить(ТаблицаДанных) Экспорт
    Набор = СоздатьНаборЗаписей();
    Набор.Загрузить(ТаблицаДанных);
    Набор.Записать(Истина);
КонецПроцедуры

После этого в любом месте конфигурации вы сможете работать с регистром сведений точно так же легко и прозрачно, как и с табличной частью справочника:


// Пример использования объектных методов регистра в коде:

// 1. Получаем данные в Таблицу Значений
ТЗ_Данные = РегистрыСведений.ИмяВашегоРегистра.Выгрузить();

// 2. Делаем любые манипуляции с Таблицей Значений (поиск, удаление, добавление строк)
// ...

// 3. Очищаем регистр
РегистрыСведений.ИмяВашегоРегистра.Очистить();

// 4. Записываем обновленные данные обратно
РегистрыСведений.ИмяВашегоРегистра.Загрузить(ТЗ_Данные);

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


Решение №3: Использование Хранилища Значения в константе

Проанализируем еще одну альтернативу. Если вам нужно просто хранить плоскую таблицу значений целиком от сессии к сессии, и при этом вам не требуется делать к ней сложные запросы в базе данных, можно использовать объект метаданных Константа с типом данных ХранилищеЗначения.

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


// Код для сохранения Таблицы Значений в константу:
Константы.ИмяВашейКонстанты.Установить(Новый ХранилищеЗначения(ВашаТаблицаЗначений));

// Код для извлечения Таблицы Значений из константы:
Хранилище = Константы.ИмяВашейКонстанты.Получить();
Если Хранилище <> Неопределено Тогда
    ВашаТаблицаЗначений = Хранилище.Получить();
КонецЕсли;

Преимущества данного метода: данные не засоряют структуру метаданных, сохраняются очень быстро одной операцией записи, а сама таблица восстанавливается в один клик. Нет необходимости создавать искусственные справочники-пустышки.


Решение №4: Сериализация таблицы во внешний файл на диске

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

Рассмотрим пример сохранения и восстановления таблицы значений на диске:


// Путь к файлу для сохранения данных
ПутьКФайлу = КаталогВременныхФайлов() + "temp_table_data.dat";

// 1. Сохранение таблицы значений на диск
ЗначениеВФайл(ПутьКФайлу, ВашаТаблицаЗначений);

// 2. Восстановление таблицы значений из файла при запуске новой сессии
Если ФайлСуществует(ПутьКФайлу) Тогда
    ВашаТаблицаЗначений = ЗначениеИзФайла(ПутьКФайлу);
КонецЕсли;

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


Подведем итоги

Использование табличной части единственного элемента справочника или документа для сохранения промежуточных данных — это классический «антипаттерн» разработки в 1С. Стремление сэкономить несколько строк кода приводит к созданию архитектурного долга, который обязательно проявит себя в виде зависаний, блокировок или потери данных при превышении лимита строк.

Для правильной организации хранения данных рекомендуем придерживаться следующих правил:

← На главную