При проектировании систем на платформе 1С:Предприятие 8 часто возникает задача хранения истории изменений того или иного значения. Например, когда у элемента справочника есть реквизит (допустим, Булево), состояние которого меняется раз в месяц, и нам важно знать, какое значение было актуально в конкретный момент времени. Рассмотрим подробнее, как лучше реализовать такой механизм, проанализируем использование регистров сведений и альтернативные варианты.
Использование периодического регистра сведений — это наиболее естественный и эффективный способ для платформы 1С. В этом случае мы создаем новый объект метаданных РегистрСведений, устанавливаем для него свойство Периодичность (например, «В пределах месяца» или «День») и режим записи Независимый.
Преимущества этого подхода:
МАКСИМУМ(Период), достаточно обратиться к виртуальной таблице.Period автоматически включается в кластерный индекс. Это значительно ускоряет выборку данных по времени.Проанализируем пример кода для получения текущего состояния реквизита через «Срез последних»:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИсторияСостоянийСрезПоследних.Состояние КАК АктуальноеСостояние
|ИЗ
| РегистрСведений.ИсторияСостояний.СрезПоследних(&Период, Объект = &Объект) КАК ИсторияСостоянийСрезПоследних";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Объект", СсылкаНаСправочник);
Выборка = Запрос.Выполнить().Выбрать();
Иногда разработчики предпочитают создавать непериодический регистр, добавляя измерение ДатаИзменения (тип Дата) вручную. Рассмотрим ситуацию, когда это может показаться выгодным, и выясним причины, почему это не всегда оптимально.
Основной аргумент сторонников такого подхода — возможность «сделать лучше и оптимальней», чем стандартный механизм. Однако на практике это накладывает на программиста дополнительные обязанности:
ДатаИзменения должна быть последним или предпоследним полем в индексе.Разберем пример такого «ручного» запроса:
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| История.Состояние
|ИЗ
| РегистрСведений.ИсторияСостояний КАК История
|ГДЕ
| История.Объект = &Объект
| И История.ДатаИзменения <= &Период
|УПОРЯДОЧИТЬ ПО
| История.ДатаИзменения УБЫВ";
Такой подход допустим, но он лишает вас возможности использовать Функциональные опции, которые могут автоматически менять поведение интерфейса в зависимости от данных в периодическом регистре.
Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), можно включить стандартную подсистему «Версионирование объектов». Это позволит хранить историю изменений вообще всех реквизитов объекта без создания новых регистров.
Но здесь есть существенные нюансы:
Важный технический аспект, который мы должны рассмотреть, — это вынос реквизита из справочника в регистр сведений с точки зрения блокировок СУБД. Выясним причину, почему это полезно:
Когда вы меняете реквизит непосредственно в справочнике и вызываете Объект.Записать(), платформа блокирует всю запись в таблице справочника. Если в это время другой пользователь пытается изменить, например, наименование этого же элемента, возникнет конфликт блокировок.
Использование независимого регистра сведений позволяет избежать этой проблемы. Запись в регистр блокирует только строку в таблице регистра, оставляя сам элемент справочника доступным для редактирования другими процессами. Это критически важно для высоконагруженных систем.
При создании регистра нужно выбрать режим записи. Посмотрим на различия:
МенеджерЗаписи. Это идеально подходит для регламентных заданий, которые раз в месяц обновляют состояние.Проанализировав все варианты, мы приходим к следующим выводам: