Разработчики, начинающие глубоко изучать архитектуру платформы 1С:Предприятие, часто сталкиваются с кажущимся противоречием в официальной документации ИТС. С одной стороны, методические материалы утверждают, что для всех необъектных сущностей, подчиненных регистратору, уникальный ключ в базе данных состоит из регистратора и номера строки. С другой стороны, при попытке записать в такой регистр две строки с одинаковыми измерениями (но с разными номерами строк) в рамках одного документа платформа выдает ошибку уникальности. Давайте детально проанализируем эту ситуацию, разберем архитектуру хранения данных в СУБД и выясним, почему на практике «это не работает» так, как многие ожидают.
Для начала разберем, как платформа 1С проектирует таблицы в СУБД (будь то MS SQL Server, PostgreSQL или IBM DB2) для регистров сведений, подчиненных регистратору. На уровне базы данных для таблицы такого регистра создается уникальный кластерный индекс. В терминах SQL он состоит из следующих полей:
_RecorderTRef — тип регистратора (идентификатор таблицы метаданных документа)._RecorderRRef — уникальный идентификатор (UUID) конкретного документа-регистратора._LineNo — номер строки движения.Этот индекс физически упорядочивает данные на диске. Платформа 1С работает с движениями документов исключительно пакетами — через объект РегистрСведенийНаборЗаписей. При проведении документа или отмене проведения платформа должна мгновенно удалить все старые записи по этому регистратору и записать новые. Наличие физического уникального кластерного индекса по полям регистратора и номера строки позволяет СУБД выполнять эти операции за доли миллисекунды.
Таким образом, с точки зрения СУБД, уникальным ключом физической строки в таблице базы данных действительно является связка Регистратор + Номер строки. В СУБД физически невозможно записать две строки с абсолютно одинаковым регистратором и одинаковым номером строки.
Почему же тогда на практике мы не можем записать в один документ две строки с одинаковыми измерениями, даже если у них будут разные номера строк (например, строка 1 и строка 2)?
Дело в том, что регистр сведений — это прикладной объект, предназначенный для хранения информации в разрезе определенных измерений (координат). По своей сути регистр сведений должен гарантировать, что для одного набора измерений в один момент времени существует только одно значение ресурса. Чтобы обеспечить это прикладное правило, платформа 1С принудительно создает в базе данных дополнительный уникальный некластерный индекс по измерениям регистра:
_Period (если регистр периодический) + _Fld<ИмяИзмерения1> + _Fld<ИмяИзмерения2> и так далее.Когда мы пытаемся записать набор записей регистра сведений, СУБД выполняет проверку всех существующих индексов. Проанализируем по шагам, что происходит при попытке записи дублирующихся измерений:
Номенклатура имеет значение «Карандаш». Номер первой строки — 1, второй строки — 2.Номенклатура). И здесь СУБД обнаруживает, что мы пытаемся записать две строки с одинаковым значением «Карандаш».Именно поэтому для разработчика логическим ключом уникальности регистра сведений всегда является набор его измерений (плюс период для периодических регистров).
Для того чтобы окончательно закрепить этот материал, сравним поведение регистра сведений с регистрами накопления. В регистрах накопления, накопления по партиям или проводках бухгалтерии нет понятия уникальности измерений. В один документ мы можем добавить сколько угодно строк с одинаковым товаром и одинаковыми аналитиками. Для них уникальный некластерный индекс по измерениям в СУБД просто не создается.
В регистрах накопления единственным критерием уникальности строки в базе данных является именно физический ключ Регистратор + НомерСтроки. В регистрах сведений же действует двойной контроль уникальности: физический (по регистратору и номеру) и логический (по измерениям).
Рассмотрим ситуацию, когда у периодического регистра сведений установлена периодичность По позиции регистратора. Как в этом случае меняется логика работы индексов?
В этом сценарии уникальный индекс по измерениям в СУБД расширяется платформой. В него включаются не только измерения и период, но также регистратор и номер строки. Это делается для того, чтобы разные документы могли делать записи на одну и ту же секунду (поскольку позиция регистратора уникальна в пределах оси времени).
Однако даже при такой настройке внутри одного и того же документа записать дубли по измерениям не получится. В рамках одного регистратора период (момент времени) у всех строк будет абсолютно одинаковым, а значит, при попытке записи одинаковых измерений некластерный индекс все равно будет нарушен.
Что делать разработчику, если бизнес-логика задачи требует хранить в подчиненном регистре сведений несколько записей с одинаковыми измерениями в рамках одного документа? Рассмотрим основные варианты решения этой задачи.
Самый простой и распространенный способ — добавить в регистр сведений еще одно измерение, которое будет гарантировать уникальность каждой строки. В качестве такого измерения обычно используют:
КлючСвязи (тип Число) — в него записывается номер строки табличной части документа, к которой относятся данные движения.ИдентификаторСтроки (тип УникальныйИдентификатор или строка GUID).Рассмотрим пример кода при заполнении набора записей с использованием измерения КлючСвязи:
НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);
Для Каждого СтрокаТЧ Из СсылкаНаДокумент.Товары Цикл
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Регистратор = СсылкаНаДокумент;
НоваяЗапись.Период = СсылкаНаДокумент.Дата;
// Заполняем измерения
НоваяЗапись.Номенклатура = СтрокаТЧ.Номенклатура;
НоваяЗапись.КлючСвязи = СтрокаТЧ.НомерСтроки; // Обеспечивает уникальность измерений!
// Заполняем ресурсы
НоваяЗапись.Значение = СтрокаТЧ.ЗначениеСвойства;
КонецЦикла;
НаборЗаписей.Записать();
Поскольку у каждой записи теперь будет уникальное значение в измерении КлючСвязи, некластерный индекс СУБД по измерениям не будет нарушен, и платформа успешно запишет данные.
Если специфика данных предполагает частые дубликаты измерений и накопление числовых показателей (например, объемы поставок, суммы, количества), то использование регистра сведений является архитектурной ошибкой. В этом случае правильным решением будет перепроектировать систему и использовать Регистр накопления (оборотов или остатков), где дублирование измерений разрешено на уровне платформы.
Подведем итог нашему анализу: