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