Как перенести общий реквизит регистра в обычное измерение на больших объемах данных без длительного простоя?

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

При развитии крупных информационных систем на платформе 1С:Предприятие 8 часто возникает необходимость изменения архитектуры базы данных. Типичный сценарий — отказ от использования общего реквизита (например, выполнявшего роль разделителя доступа) в конкретном регистре накопления с сохранением накопленных данных и их переносом в обычное измерение регистра.

Главная сложность этой задачи на высоконагруженных базах с терабайтными объемами данных заключается в жестком ограничении на время простоя (технологического окна). Стандартная реструктуризация силами платформы 1С может занять десятки часов или даже дней, полностью заблокировав работу пользователей. В этой статье мы подробно разберем, как устроен этот процесс изнутри, проанализируем риски «быстрого» изменения через СУБД и рассмотрим действительно безопасные альтернативные подходы.

Физическая структура: как общие реквизиты хранятся в SQL

Для принятия правильного технического решения нам необходимо четко понимать, как платформа 1С проецирует метаданные на физические таблицы СУБД. Существует распространенное заблуждение, что общие реквизиты хранятся в некой отдельной связанной таблице. На самом деле это не так:

  1. В физической таблице движений регистра накопления (например, _AccumRg123) общий реквизит создается как самая обычная колонка (с физическим именем вида _Fld123_RRRef).
  2. Если для общего реквизита включено свойство «Разделение данных» (режим «Разделять»), платформа СУБД автоматически модифицирует все основные индексы этой таблицы, помещая колонку разделителя на самую первую позицию. Это необходимо для обеспечения эффективной фильтрации на уровне СУБД.
  3. При отключении общего реквизита или его выводе из состава регистра платформа должна физически перестроить индексы и удалить (или очистить) колонку, что на больших таблицах вызывает колоссальную нагрузку на дисковую подсистему и процессор СУБД.

Опасный путь: разбор схемы прямого изменения через SQL

На форумах часто предлагают «хакерский» способ быстрого решения проблемы в обход штатных механизмов 1С. Давайте проанализируем этот алгоритм, предложенный в сообщениях (6) и (8):

  1. Переименовать исходную таблицу регистра (например, _AccumRg123) на уровне SQL-сервера.
  2. В конфигураторе 1С добавить новое измерение в регистр и исключить из его состава общий реквизит.
  3. Провести обновление конфигурации базы данных (реструктуризация пройдет мгновенно, так как 1С создаст новую пустую таблицу).
  4. Удалить созданную пустую таблицу на уровне SQL.
  5. Вернуть переименованной исходной таблице ее оригинальное имя.
  6. С помощью прямого SQL-запроса UPDATE или INSERT INTO ... SELECT скопировать данные из старой колонки общего реквизита в колонку нового измерения.

ВНИМАНИЕ! Этот способ несет в себе критические риски для работоспособности базы данных:

Во-первых, вы рискуете полностью нарушить целостность внутренней таблицы DBSchema, в которой платформа 1С хранит точное описание структуры базы данных (включая состав индексов, типы полей и соответствие имен). Несовпадение реальной физической структуры в SQL и описания в DBSchema гарантированно приведет к ошибкам при последующих обновлениях конфигурации или выполнении операции «Тестирование и исправление».

Во-вторых, регистр накопления остатков состоит из двух таблиц: таблицы движений (_AccumRg123) и таблицы итогов (_AccumRgT123). Измерение регистра — это разрез учета, по которому СУБД рассчитывает и хранит остатки. Если вы просто скопируете данные в таблицу движений на уровне SQL, таблица итогов останется неверной. Все виртуальные таблицы остатков начнут выдавать некорректные и искаженные данные. Для исправления ситуации вам все равно придется запускать полный пересчет итогов средствами 1С, что на больших объемах нивелирует всю экономию времени от использования SQL-скриптов.

Безопасный метод А. Порционный фоновый перенос данных

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

Разберем по шагам реализацию этого метода:

Шаг 1. Подготовка метаданных в конфигураторе

В конфигураторе добавьте в регистр накопления новое измерение. Очень важно на этом этапе временно снять флаг «Запрет незаполненных значений» для этого измерения. Это позволит платформе успешно записывать новые движения с незаполненным измерением. Обновите конфигурацию базы данных. Данное обновление пройдет быстро, так как добавление новой колонки без пересчета существующих данных не требует полной перестройки таблицы СУБД.

Шаг 2. Разработка и запуск фонового переноса

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


// Процедура запускает порционный перенос данных в фоновом режиме
Процедура ВыполнитьПереносДанных() Экспорт
    
    ИмяРегистра = "РегистрНакопления.ТоварыНаСкладах";
    РазмерПорции = 1000; // Оптимальный размер порции для минимизации блокировок
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ " + Формат(РазмерПорции, "ЧГ=0") + "
        |	Регистр.Регистратор КАК Регистратор
        |ИЗ
        |	РегистрНакопления.ТоварыНаСкладах КАК Регистр
        |ГДЕ
        |	Регистр.НовоеИзмерение = ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)
        |	И Регистр.ОбщийРеквизитОрганизация <> ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)";
        
    Выборка = Запрос.Выполнить().Выбрать();
    
    Если Не Выборка.Следующий() Тогда
        ЗаписатьВЛог("Перенос данных успешно завершен или нет данных для обработки.");
        Возврат;
    КонецЕсли;
    
    СписокРегистраторов = Новый Массив;
    Выборка.Сбросить();
    
    Пока Выборка.Следующий() Цикл
        СписокРегистраторов.Добавить(Выборка.Регистратор);
    КонецЦикла;
    
    // Обрабатываем порцию регистраторов в транзакции
    НачатьТранзакцию();
    Попытка
        Для Каждого ДокументСсылка Из СписокРегистраторов Цикл
            Набор = РегистрыНакопления.ТоварыНаСкладах.СоздатьНаборЗаписей();
            Набор.Отбор.Регистратор.Установить(ДокументСсылка);
            Набор.Прочитать();
            
            Для Каждого Запись Из Набор Цикл
                // Копируем значение из общего реквизита в новое измерение
                Запись.НовоеИзмерение = Запись.ОбщийРеквизитОрганизация;
            КонецЦикла;
            
            // Записываем набор, отключая лишние проверки при необходимости
            Набор.Записать(Истина);
        КонецДля;
        ЗафиксироватьТранзакцию();
        ЗаписатьВЛог("Успешно обработана порция из " + СписокРегистраторов.Количество() + " документов.");
    Исключение
        ОтменитьТранзакцию();
        ЗаписатьВЛог("Ошибка при обработке порции: " + ОписаниеОшибки());
        ВызватьИсключение; // Прерываем выполнение фонового задания для анализа
    КонецПопытки;
    
КонецПроцедуры

Запуск этой обработки можно настроить в виде фонового задания с интервалом в несколько секунд. Пользователи смогут продолжать беспрепятственно работать в системе, так как блокировка таблиц на период записи небольшого пакета документов (до 1000 штук) будет практически незаметна.

Шаг 3. Финальная синхронизация и переключение

Когда фоновое задание обработает основную историческую массу данных, вам потребуется организовать короткое технологическое окно (обычно от 15 до 30 минут):

  1. Завершите сеансы пользователей в базе данных.
  2. Запустите финальный перенос данных за период, пока пользователи работали во время выполнения фонового задания. Так как объем новых данных минимален, этот шаг займет считанные минуты.
  3. В конфигураторе исключите общий реквизит из состава регистра накопления и установите для нового измерения флаг «Запрет незаполненных значений» (если бизнес-логика требует обязательного заполнения этого разреза).
  4. Запустите обновление конфигурации базы данных.

Шаг 4. Восстановление итогов

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


// Программный запуск пересчета итогов для конкретного регистра
Процедура ПересчитатьИтогиРегистра()
    ИмяРегистра = "ТоварыНаСкладах";
    ЗаписатьВЛог("Начало пересчета итогов для регистра: " + ИмяРегистра);
    РегистрыНакопления[ИмяРегистра].ПересчитатьИтоги();
    ЗаписатьВЛог("Пересчет итогов успешно завершен.");
КонецПроцедуры

Безопасный метод Б. Использование фонового обновления конфигурации базы данных

Начиная с современных версий платформы 1С:Предприятие 8.3, разработчикам доступна встроенная технология «Фоновое обновление конфигурации базы данных». Этот механизм разработан специально для минимизации времени простоя крупных баз данных.

При использовании этого метода процесс выглядит следующим образом:

  1. Вы вносите изменения в конфигурацию (добавляете измерение, отключаете общий реквизит).
  2. Запускаете фоновое обновление базы данных. Платформа 1С в фоновом режиме создает новые временные таблицы в СУБД, копирует туда исторические данные и подготавливает новые индексы. При этом пользователи продолжают полноценно работать с системой на чтение и запись.
  3. Все изменения, которые вносят пользователи во время подготовки фонового обновления, автоматически фиксируются платформой и накапливаются в промежуточных таблицах регистрации изменений.
  4. По завершении фоновой подготовки вам потребуется монопольно заблокировать базу всего на несколько минут (иногда секунд). В этот момент платформа применит подготовленные таблицы СУБД взамен старых и зафиксирует изменения структуры.

Важный аспект: последствия отключения общего реквизита для безопасности (RLS)

Если изменяемый общий реквизит использовался в вашей конфигурации как разделитель доступа (в рамках механизма RLS — Row Level Security), помните о критически важных последствиях его отключения:

Как только общий реквизит исключается из состава регистра, платформа 1С перестает автоматически накладывать неявные фильтры во всех запросах к этому регистру на уровне СУБД. Это означает, что если вы не перепишете правила RLS для этого регистра с использованием нового измерения, пользователи мгновенно увидят данные других областей (например, чужих организаций или филиалов), доступ к которым для них должен быть закрыт.

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

← На главную