При проектировании систем учета в 1С разработчики часто сталкиваются с классической задачей: необходимо вести историю изменений какого-либо объекта (например, закрепления оборудования за рабочими местами или сотрудниками) и при этом иметь возможность быстро получать только актуальные данные на текущий момент. Основная сложность возникает, когда объект выбывает из учета (списывается, увольняется). Как сделать так, чтобы история перемещений сохранилась, но в актуальном срезе последних выбывшие объекты больше не фигурировали и не нагружали базу данных?
В этой статье мы подробно разберем архитектурные ошибки, которые часто совершают при решении этой задачи, проанализируем различные варианты проектирования структуры данных и выберем наиболее эффективные паттерны разработки на платформе 1С:Предприятие 8.3.
Давайте проанализируем структуру регистра, предложенную автором темы в самом начале. Он выбрал периодический регистр сведений с двумя измерениями: РабочееМесто и ОС (Основное средство). Это классическая ошибка проектирования связей в базах данных.
Два измерения в регистре сведений создают связь вида "Многие-ко-Многим". Такая структура предполагает, что одно и то же уникальное основное средство может одновременно находиться на нескольких рабочих местах, а на одном рабочем месте может одновременно находиться множество различных ОС. Если второе утверждение верно (на одном столе могут стоять монитор, системный блок и принтер), то первое утверждение в реальном учете физически невозможно: уникальный компьютер с конкретным серийным номером не может одновременно стоять в двух разных кабинетах.
Следовательно, связь между основным средством и рабочим местом является связью вида "Многие-к-Одному" (у одного рабочего места может быть много ОС, но у каждого ОС может быть только одно рабочее место в конкретный момент времени). Для реализации такой связи в 1С применяется другое правило:
Уникальный объект (в данном случае ОС) должен быть единственным измерением регистра, а место его нахождения (РабочееМесто) должно быть ресурсом.
Если мы оставим оба объекта в измерениях, то при перемещении оборудования нам придется решать сложнейшие задачи по "закрытию" старых записей, иначе компьютер зависнет сразу на двух рабочих местах одновременно. Если же ОС является измерением, а РабочееМесто — ресурсом, то при записи нового движения на новую дату старое местоположение автоматически перезапишется в срезе последних, так как ключ уникальности записи регистра (Период + Измерения) содержит только само основное средство.
Теперь рассмотрим ключевой вопрос автора: что происходит при уходе (списании) ОС и как сделать так, чтобы в срезе последних не было этой записи?
Виртуальная таблица СрезПоследних строится по основной физической таблице регистра сведений. До тех пор, пока в регистре существует хотя бы одна запись по конкретному измерению (нашему ОС), эта запись гарантированно попадет в срез последних. Физически удалить запись из базы мы не можем, так как нам необходимо сохранить историю перемещений этого компьютера в прошлом (для отчетов за предыдущие периоды).
Давайте разберем три профессиональных паттерна решения этой проблемы.
Самый простой и быстрый в реализации способ — добавить в периодический регистр сведений дополнительный признак, определяющий статус нахождения оборудования в эксплуатации. Назовем структуру нашего регистра следующим образом:
ОС (Тип: СправочникСсылка.ОсновныеСредства)РабочееМесто (Тип: СправочникСсылка.РабочиеМеста)СтатусУчета (Тип: ПеречислениеСсылка.СтатусыПользованияОборудованием — значения: "ВРаботе", "Списано", "ВРезерве") или просто Булево флаг Выбыл.Когда оборудование списывается или утилизируется, документ-регистратор делает новую запись в этот регистр с текущим периодом (датой списания). При этом в ресурс РабочееМесто записывается пустая ссылка, а в ресурс Выбыл устанавливается значение Истина (или СтатусУчета принимает значение Списано).
В результате история сохраняется. Если же нам нужно получить список только реально работающего на данный момент оборудования, мы пишем простой и быстрый запрос к срезу последних с наложением фильтра по ресурсу:
ВЫБРАТЬ
МестонахождениеОССрезПоследних.ОС КАК ОС,
МестонахождениеОССрезПоследних.РабочееМесто КАК РабочееМесто
ИЗ
РегистрСведений.МестонахождениеОС.СрезПоследних(&Период, ) КАК МестонахождениеОССрезПоследних
ГДЕ
НЕ МестонахождениеОССрезПоследних.Выбыл
Этот запрос мгновенно отсечет все списанные основные средства. При этом на уровне СУБД срез последних будет работать очень эффективно.
В крупных тиражных решениях от 1С (например, в 1С:ERP) используется более масштабируемый подход. Вместо одного универсального регистра создаются два специализированных:
ОС, а ресурсами — текущее рабочее место и статус его активности. При списании ОС запись из этого регистра полностью физически удаляется (или перезаписывается). Соответственно, этот регистр всегда остается "чистым", легким и содержит только те данные, которые эксплуатируются прямо сейчас.Давайте посмотрим, как в таком случае будет выглядеть получение данных о текущей дислокации работающих ОС. Мы просто выбираем данные из непериодического регистра состояний:
ВЫБРАТЬ
АктуальноеСостояниеОС.ОС,
АктуальноеСостояниеОС.РабочееМесто
ИЗ
РегистрСведений.АктуальноеСостояниеОС КАК АктуальноеСостояниеОС
ГДЕ
АктуальноеСостояниеОС.Состояние <> ЗНАЧЕНИЕ(Перечисление.СостоянияОС.Списано)
Этот подход гарантирует максимальную производительность системы, так как нам вообще не нужно обращаться к тяжелым расчетам виртуальной таблицы СрезПоследних для получения оперативной информации.
Этот современный паттерн проектирования активно применяется разработчиками фирмы 1С в конфигурации "Зарплата и управление персоналом" для хранения различных периодических состояний (например, плановых начислений или графиков работы сотрудников).
Мы создаем непериодический регистр сведений, но добавляем в него два обязательных реквизита даты: ДатаНачала и ДатаОкончания.
Рассмотрим пример заполнения такого регистра:
ДатаНачала = 01.01.2023, ДатаОкончания = 31.12.3999 (или пустая дата, символизирующая бесконечность).ДатаОкончания = 14.05.2023 23:59:59, и создаем вторую запись: ДатаНачала = 15.05.2023, ДатаОкончания = 31.12.3999.ДатаОкончания фактическую дату списания (например, 10.11.2023). Новую запись при этом мы не создаем!Чтобы получить список работающих основных средств на любую произвольную дату (например, на сегодня), нам больше не нужна виртуальная таблица среза последних. Мы пишем очень простой и чрезвычайно быстрый запрос к физической таблице:
ВЫБРАТЬ
ИнтервальныйУчетОС.ОС КАК ОС,
ИнтервальныйУчетОС.РабочееМесто КАК РабочееМесто
ИЗ
РегистрСведений.ИнтервальныйУчетОС КАК ИнтервальныйУчетОС
ГДЕ
&Период МЕЖДУ ИнтервальныйУчетОС.ДатаНачала И ИнтервальныйУчетОС.ДатаОкончания
Поскольку у выбывшего ОС интервал времени закрыт датой списания, условие МЕЖДУ на текущую дату вернет ложь, и этот компьютер автоматически не попадет в выборку. При этом вся история перемещений остается в базе в виде красивых, непересекающихся временных интервалов.
В конце обсуждения автор темы решил отказаться от регистра сведений и сделать систему на основе двух регистров накопления остатков. Давайте выясним причину, почему это решение является грубой архитектурной ошибкой при поштучном учете уникальных объектов.
Автор рассуждал так: "При списании я сделаю движение по регистру накопления "в минус", остаток станет равным нулю, и запись автоматически исчезнет из таблицы остатков".
Это опасное заблуждение. Давайте разберем, как устроена работа СУБД с регистрами накопления остатков в 1С:
_Totals).ОС в ноль, платформа 1С не удаляет физическую строку из таблицы итогов СУБД! Строка с нулевыми значениями ресурсов продолжает находиться в базе данных.Таким образом, использование регистра накопления не решает проблему "лишних" записей в базе данных. Напротив, оно приводит к неоправданному росту таблиц итогов, существенно замедляет проведение документов и усложняет получение простой истории изменений, для которой регистры накопления изначально не предназначены.
Если вы выбрали первый вариант (периодический регистр сведений с флагом выбытия), важно знать о системных механизмах оптимизации, которые предоставляет современная платформа 1С:Предприятие 8.3 (начиная с версии 8.3.11).
В свойствах периодического регистра сведений в Конфигураторе присутствует настройка "Разрешить итоги: Срез последних".
Давайте посмотрим, что дает включение этой опции:
_RG). В ней хранится ровно одна (самая последняя) запись по каждому уникальному измерению.СрезПоследних без указания конкретной даты (то есть на текущее время), СУБД не рассчитывает срез по всей исторической таблице, содержащей миллионы строк. Она мгновенно считывает данные напрямую из готовой таблицы итогов.Благодаря этому механизму, даже если ваше предприятие имеет миллионы записей истории перемещений оборудования, получение текущих остатков с использованием условия ГДЕ НЕ Выбыл будет выполняться за доли секунды. Главное — не забывать накладывать отборы по измерениям непосредственно в параметрах виртуальной таблицы среза последних, а не во внешнем условии ГДЕ запроса.