В процессе разработки корпоративных информационных систем на платформе «1С:Предприятие» перед архитектором часто встает задача проектирования подсистемы жизненного цикла документов. Нам требуется вести учет различных состояний (например, «Заказ: создан, оплачен, собран, отгружен» или «Реализация: подготовлена, оригиналы получены»). При этом система должна работать быстро, не перегружать СУБД, позволять строить аналитические отчеты по истории изменений и корректно работать в условиях закрытых периодов.
В данной статье мы подробно разберем, как спроектировать такую подсистему, решим дилемму выбора между реквизитом документа и регистром сведений, определим оптимальный тип данных для измерений и рассмотрим лучшие практики оптимизации производительности, включая гибридные подходы и новые возможности платформы.
Первый вопрос, с которым сталкивается разработчик: где физически хранить статус документа? Существует два классических подхода, каждый из которых имеет свои преимущества и критические недостатки. Проанализируем их подробнее.
Подход 1: Хранение статуса в реквизите самого документа
При таком подходе в метаданные каждого документа (например, ДокументСсылка.ЗаказПокупателя) добавляется реквизит Статус с типом ссылки на соответствующее перечисление или справочник.
Преимущества:
Недостатки:
Подход 2: Хранение статусов в регистре сведений
Этот подход предполагает создание независимого (или подчиненного регистратору) регистра сведений (например, РегистрСведений.СтатусыДокументов), где ключевым измерением является ссылка на документ, а ресурсом — его статус.
Преимущества:
СтатусыДокументов).Недостатки:
Рассмотрим, как правильно спроектировать структуру такого регистра сведений, чтобы избежать проблем с производительностью на больших объемах данных.
Выясним, какой тип данных назначить для измерения Документ в нашем регистре сведений, если отслеживать статусы требуется для множества разных типов документов (заказы, реализации, счета, поступления).
1. Почему нельзя использовать План видов характеристик (ПВХ)?
Некоторые начинающие разработчики путают назначение объектов метаданных и предлагают использовать ПВХ в качестве типа измерения. Это грубая архитектурная ошибка. План видов характеристик предназначен для хранения метаданных о свойствах (например, «Цвет», «Размер», «Диагональ»), а не для хранения ссылок на физические документы информационной базы. Для идентификации документов ПВХ неприменим.
2. Проблема составного типа данных «в лоб»
Если мы просто укажем в качестве типа измерения составной тип, выбрав галочками все необходимые документы конфигурации, мы столкнемся с серьезными техническими проблемами на уровне СУБД:
INSERT/UPDATE).LEFT JOIN) со ВСЕМИ таблицами документов, входящими в этот составной тип. Если типов документов в системе десятки, производительность запроса упадет до критического минимума.3. Лучшее решение: Использование Определяемых типов
Для централизации и оптимизации работы с составными типами в платформе 1С предусмотрен специальный объект метаданных — ОпределяемыйТип. Создадим определяемый тип (например, ОпределяемыйТип.ДокументСоСтатусом) и настроим его состав:
Документ укажем тип ОпределяемыйТип.ДокументСоСтатусом.Это позволит нам гибко расширять список контролируемых документов в будущем без изменения структуры самого регистра сведений и минимизирует количество неявных соединений в запросах.
Для эффективного ведения статусов создадим независимый периодический регистр сведений СтатусыДокументов со следующей структурой:
Секунда (позволит отслеживать историю изменений во времени).Независимый.Документ — Тип: ОпределяемыйТип.ДокументСоСтатусом. Свойство Индексировать: Индексировать (критически важно для ускорения поиска по документу).Статус — Тип: ПеречислениеСсылка.СтатусыДокументов (или СправочникСсылка.ЭтапыБизнесПроцессов, если состав статусов должен настраиваться пользователями в режиме предприятия).АвторИзменения — Тип: СправочникСсылка.Пользователи.Примечание — Тип: Строка (неограниченной длины или фиксированной длины для хранения комментариев оператора).Если мы будем выводить статус документа в форму списка (например, в список заказов покупателей), получая текущее состояние через левое соединение таблицы документов с виртуальной таблицей СрезПоследних нашего регистра сведений, интерфейс начнет «тормозить» при скроллинге, поиске и отборах. Виртуальные таблицы срезов последних в соединениях — крайне тяжелая операция для СУБД на больших объемах данных.
Чтобы обойти это ограничение, применим гибридный архитектурный подход:
Статус (без признака проведения, чтобы его изменение не влияло на движения документа). Он будет использоваться исключительно для быстрого вывода в динамические списки и простых отборов.СтатусыДокументов. Это сохраняет историю изменений.Статус в самом документе. Чтобы обойти проблему закрытых периодов и не вызывать блокировки, мы записываем документ с установленным признаком ОбменДанными.Загрузка = Истина.Рассмотрим пример кода, реализующий этот гибридный механизм при изменении статуса заказа:
Процедура УстановитьНовыйСтатусДокумента(СсылкаНаДокумент, НовыйСтатус, Автор, Комментарий = "") Экспорт
// Шаг 1. Запись истории в периодический регистр сведений
НаборЗаписей = РегистрыСведений.СтатусыДокументов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Документ.Установить(СсылкаНаДокумент);
// Поскольку регистр периодический, мы не перезаписываем старые, а добавляем новую запись
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = ТекущаяДатаСеанса();
НоваяЗапись.Документ = СсылкаНаДокумент;
НоваяЗапись.Статус = НовыйСтатус;
НоваяЗапись.АвторИзменения = Автор;
НоваяЗапись.Примечание = Комментарий;
Попытка
НаборЗаписей.Записать(Ложь); // Параметр Ложь указывает на добавление записей, а не перезапись всего набора по отбору
Исключение
ЗаписьЖурналаРегистрации("Установка статуса", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение "Не удалось записать историю изменения статуса!";
КонецПопытки;
// Шаг 2. Обновление реквизита в самом документе для оптимизации динамических списков
// Обходим блокировки закрытого периода и оптимизируем скорость записи
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокументОбъект.Статус <> НовыйСтатус Тогда
ДокументОбъект.Статус = НовыйСтатус;
// Включаем режим загрузки, чтобы отключить проверки проведения и подписки на события,
// которые могут заблокировать запись в закрытом периоде
ДокументОбъект.ОбменДанными.Загрузка = Истина;
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
ЗаписьЖурналаРегистрации("Установка статуса в документе", УровеньЖурналаРегистрации.Предупреждение,,, ОписаниеОшибки());
// Даже если объект заблокирован намертво СУБД, у нас осталась запись в регистре сведений
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Если ваша база данных развернута на современных версиях платформы (8.3.25+), обязательно воспользуйтесь новыми системными механизмами оптимизации записи больших таблиц.
Режим замещения при записи (Merge)
Для независимых регистров сведений в новых версиях платформы оптимизирован механизм записи наборов данных. При использовании метода Записать() платформа использует СУБД-специфичные инструкции слияния (например, MERGE в MS SQL Server или INSERT ... ON CONFLICT в PostgreSQL). Это позволяет не очищать предварительно всю таблицу по отбору перед вставкой новых данных, а обновлять существующие и дозаписывать новые записи в один проход, что кардинально экономит транзакционные ресурсы сервера баз данных.
Режим сокращения типов при реструктуризации
Если со временем список документов, для которых ведется учет статусов, сокращается, платформа позволяет эффективно управлять освобождением места. В свойствах метаданных при изменении состава ОпределяемогоТипа используйте свойство «Режим сокращения типа» в значение «Удаление данных». Платформа при реструктуризации базы данных автоматически очистит устаревшие записи физических таблиц СУБД, предотвращая накопление «мусорных» данных и уменьшая размер индексов регистра.
Мы проанализировали подходы к реализации контроля состояний бизнес-объектов в системе «1С:Предприятие». Подводя итоги, выделим ключевые правила построения подсистемы:
ОбменДанными.Загрузка = Истина.