Является ли номер строки ключом уникальности у регистра сведений, подчиненного регистратору?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработчики, начинающие глубоко изучать архитектуру платформы 1С:Предприятие, часто сталкиваются с кажущимся противоречием в официальной документации ИТС. С одной стороны, методические материалы утверждают, что для всех необъектных сущностей, подчиненных регистратору, уникальный ключ в базе данных состоит из регистратора и номера строки. С другой стороны, при попытке записать в такой регистр две строки с одинаковыми измерениями (но с разными номерами строк) в рамках одного документа платформа выдает ошибку уникальности. Давайте детально проанализируем эту ситуацию, разберем архитектуру хранения данных в СУБД и выясним, почему на практике «это не работает» так, как многие ожидают.

Физический уровень СУБД: Кластерный индекс

Для начала разберем, как платформа 1С проектирует таблицы в СУБД (будь то MS SQL Server, PostgreSQL или IBM DB2) для регистров сведений, подчиненных регистратору. На уровне базы данных для таблицы такого регистра создается уникальный кластерный индекс. В терминах SQL он состоит из следующих полей:

  1. _RecorderTRef — тип регистратора (идентификатор таблицы метаданных документа).
  2. _RecorderRRef — уникальный идентификатор (UUID) конкретного документа-регистратора.
  3. _LineNo — номер строки движения.

Этот индекс физически упорядочивает данные на диске. Платформа 1С работает с движениями документов исключительно пакетами — через объект РегистрСведенийНаборЗаписей. При проведении документа или отмене проведения платформа должна мгновенно удалить все старые записи по этому регистратору и записать новые. Наличие физического уникального кластерного индекса по полям регистратора и номера строки позволяет СУБД выполнять эти операции за доли миллисекунды.

Таким образом, с точки зрения СУБД, уникальным ключом физической строки в таблице базы данных действительно является связка Регистратор + Номер строки. В СУБД физически невозможно записать две строки с абсолютно одинаковым регистратором и одинаковым номером строки.

Логический уровень 1С: Проверка уникальности измерений

Почему же тогда на практике мы не можем записать в один документ две строки с одинаковыми измерениями, даже если у них будут разные номера строк (например, строка 1 и строка 2)?

Дело в том, что регистр сведений — это прикладной объект, предназначенный для хранения информации в разрезе определенных измерений (координат). По своей сути регистр сведений должен гарантировать, что для одного набора измерений в один момент времени существует только одно значение ресурса. Чтобы обеспечить это прикладное правило, платформа 1С принудительно создает в базе данных дополнительный уникальный некластерный индекс по измерениям регистра:

Когда мы пытаемся записать набор записей регистра сведений, СУБД выполняет проверку всех существующих индексов. Проанализируем по шагам, что происходит при попытке записи дублирующихся измерений:

  1. Программа формирует набор записей с двумя строками. У обеих строк измерение Номенклатура имеет значение «Карандаш». Номер первой строки — 1, второй строки — 2.
  2. СУБД проверяет кластерный индекс (Регистратор + Номер строки). Строка 1 и Строка 2 уникальны. Проверка пройдена.
  3. СУБД проверяет некластерный индекс по измерениям (в нашем случае — по полю Номенклатура). И здесь СУБД обнаруживает, что мы пытаемся записать две строки с одинаковым значением «Карандаш».
  4. СУБД возвращает ошибку нарушения уникального индекса, а платформа 1С транслирует ее пользователю: «Запись с такими ключевыми полями существует!».

Именно поэтому для разработчика логическим ключом уникальности регистра сведений всегда является набор его измерений (плюс период для периодических регистров).

Сравнение с регистрами накопления и бухгалтерии

Для того чтобы окончательно закрепить этот материал, сравним поведение регистра сведений с регистрами накопления. В регистрах накопления, накопления по партиям или проводках бухгалтерии нет понятия уникальности измерений. В один документ мы можем добавить сколько угодно строк с одинаковым товаром и одинаковыми аналитиками. Для них уникальный некластерный индекс по измерениям в СУБД просто не создается.

В регистрах накопления единственным критерием уникальности строки в базе данных является именно физический ключ Регистратор + НомерСтроки. В регистрах сведений же действует двойной контроль уникальности: физический (по регистратору и номеру) и логический (по измерениям).

Особый случай: Периодичность «По позиции регистратора»

Рассмотрим ситуацию, когда у периодического регистра сведений установлена периодичность По позиции регистратора. Как в этом случае меняется логика работы индексов?

В этом сценарии уникальный индекс по измерениям в СУБД расширяется платформой. В него включаются не только измерения и период, но также регистратор и номер строки. Это делается для того, чтобы разные документы могли делать записи на одну и ту же секунду (поскольку позиция регистратора уникальна в пределах оси времени).

Однако даже при такой настройке внутри одного и того же документа записать дубли по измерениям не получится. В рамках одного регистратора период (момент времени) у всех строк будет абсолютно одинаковым, а значит, при попытке записи одинаковых измерений некластерный индекс все равно будет нарушен.

Практические решения: Как обойти ограничение уникальности

Что делать разработчику, если бизнес-логика задачи требует хранить в подчиненном регистре сведений несколько записей с одинаковыми измерениями в рамках одного документа? Рассмотрим основные варианты решения этой задачи.

Вариант 1. Использование дополнительного измерения-ключа

Самый простой и распространенный способ — добавить в регистр сведений еще одно измерение, которое будет гарантировать уникальность каждой строки. В качестве такого измерения обычно используют:

Рассмотрим пример кода при заполнении набора записей с использованием измерения КлючСвязи:


НаборЗаписей = РегистрыСведений.ЗначенияСвойствОбъектов.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);

Для Каждого СтрокаТЧ Из СсылкаНаДокумент.Товары Цикл
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Регистратор = СсылкаНаДокумент;
    НоваяЗапись.Период = СсылкаНаДокумент.Дата;
    
    // Заполняем измерения
    НоваяЗапись.Номенклатура = СтрокаТЧ.Номенклатура;
    НоваяЗапись.КлючСвязи = СтрокаТЧ.НомерСтроки; // Обеспечивает уникальность измерений!
    
    // Заполняем ресурсы
    НоваяЗапись.Значение = СтрокаТЧ.ЗначениеСвойства;
КонецЦикла;

НаборЗаписей.Записать();

Поскольку у каждой записи теперь будет уникальное значение в измерении КлючСвязи, некластерный индекс СУБД по измерениям не будет нарушен, и платформа успешно запишет данные.

Вариант 2. Смена типа регистра

Если специфика данных предполагает частые дубликаты измерений и накопление числовых показателей (например, объемы поставок, суммы, количества), то использование регистра сведений является архитектурной ошибкой. В этом случае правильным решением будет перепроектировать систему и использовать Регистр накопления (оборотов или остатков), где дублирование измерений разрешено на уровне платформы.

Резюме

Подведем итог нашему анализу:

← На главную