Периодичность и режим записи: как устроены регистры сведений 1С 8.3

· 10 мин

Содержание

Периодический регистр сведений хранит значения с привязкой к дате: у каждой записи есть поле Период, и программа умеет отдать значение «на дату». Независимый регистр сведений — это про другое: его записи создаются сами по себе, без документа-регистратора. Обе настройки задаются в конфигураторе на закладке «Данные» свойств регистра.

Частое заблуждение — считать «периодический» и «независимый» противоположностями. Это две разные оси. Свойство «Периодичность» отвечает на вопрос «нужна ли история во времени», свойство «Режим записи» — «кто пишет данные». Регистр может быть периодическим и независимым одновременно (курсы валют), непериодическим и независимым (штрихкоды), периодическим и подчинённым регистратору (цены, которые устанавливает документ).

Зачем нужен регистр сведений и чем он отличается от справочника

Регистр сведений хранит факты в разрезе нескольких измерений. Справочник хранит объекты: у элемента есть ссылка, код, наименование. У записи регистра ссылки нет — её идентифицирует набор ключевых полей.

Структура регистра сведений:

  • 📐 Измерения — разрезы, по которым ищут запись: номенклатура, валюта, склад.
  • 💰 Ресурсы — то, что хранят: цена, курс, ставка.
  • 🏷️ Реквизиты — дополнительная информация к записи, например комментарий или автор изменения.
  • 🕒 Стандартные поля — Период (у периодических), Регистратор, НомерСтроки, Активность (у подчинённых регистратору).

Главное отличие от регистров накопления: регистр сведений не складывает и не вычитает. Он хранит значение «как есть». Новая запись с тем же ключом заменяет старую, а не добавляется к ней.

Если данные нужно накапливать (остатки, обороты) — это регистр накопления. Если нужно знать «какое значение действовало» или «какое значение соответствует сочетанию разрезов» — это регистр сведений.

Периодический регистр сведений: история значений во времени

Периодичность задаётся свойством «Периодичность». Варианты:

ПериодичностьКак хранится ПериодПример применения
НепериодическийПоля Период нетШтрихкоды, соответствие кодов, настройки
В пределах секундыДата и время до секундыСобытия, статусы с точным временем
В пределах дняНачало дняКурсы валют, цены
В пределах месяцаНачало месяцаСтавки, плановые показатели
В пределах кварталаНачало кварталаКвартальные нормативы
В пределах годаНачало годаУчётная политика, годовые лимиты
По позиции регистратораМомент времени документаЗначения, которые зависят от порядка документов внутри дня

Платформа сама приводит дату к началу периода. Если у регистра периодичность «В пределах месяца», а вы записали Период = 15.03, в базе окажется 01.03. Две записи с датами 05.03 и 20.03 для одной номенклатуры превратятся в одну — вторая заменит первую.

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

Что даёт периодичность

  • 📅 Значение на любую дату. Курс на 1 марта, цена на дату документа — без хранения копий.
  • 🔍 Виртуальные таблицы СрезПоследних и СрезПервых. Они есть только у периодических регистров.
  • 🗂️ История изменений. Видно, когда и какое значение действовало.

Вариант «По позиции регистратора» доступен только регистру, подчинённому регистратору. Значение привязывается к моменту времени документа, поэтому два документа в одну секунду не конфликтуют.

Независимый регистр сведений и подчинённый регистратору

Свойство «Режим записи» имеет два значения: «Независимый» и «Подчинение регистратору».

Независимый режим

Записи вводят вручную или кодом, без документа. Пользователь открывает форму списка регистра, добавляет строку, сохраняет. Обработка загрузки курсов пишет в регистр напрямую.

  • 🔑 Ключ записи — Период (если регистр периодический) плюс все измерения.
  • 🚫 Две записи с одинаковым ключом быть не могут. При попытке записать дубль менеджером записи без замещения платформа выдаст ошибку о существующей записи с такими ключевыми полями.
  • ✏️ Отдельную запись можно прочитать, изменить и удалить через менеджер записи.

Где встречается: курсы валют, штрихкоды номенклатуры, контактная информация, настройки пользователей.

Подчинение регистратору

Записи создаёт документ при проведении. Его называют регистратором. Отменили проведение — записи удалились. Пометили документ на удаление и удалили — записи ушли вместе с ним.

  • 🔑 Ключ записи — Регистратор и НомерСтроки.
  • ⚠️ Уникальность сочетания «период + измерения» между разными документами платформа не контролирует. Два документа установят цену на одну дату — в регистре будут обе записи. Следить за этим должен разработчик.
  • 🔒 Вручную такие записи не правят: их меняют через документ.

Пример из типовых конфигураций — цены номенклатуры, которые устанавливает документ «Установка цен номенклатуры». Документ хранит, кто и когда поменял цену, а регистр даёт быстрый доступ к актуальному значению.

Если подчинённый регистр начали заполнять ещё и кодом напрямую, без документа — это ошибка проектирования. Такие записи потеряются при перепроведении регистратора или не попадут под его контроль.

Сравнение видов регистров сведений

Четыре возможных сочетания и когда каждое выбирать:

СочетаниеКлюч записиКто пишетКогда выбирать
Непериодический + независимыйИзмеренияПользователь, кодСправочные соответствия без истории: штрихкоды, настройки
Периодический + независимыйПериод + измеренияПользователь, обработкиИстория значений, которые вводят без первички: курсы, ставки
Периодический + подчинённыйРегистратор + номер строкиДокумент при проведенииЗначения, за изменением которых нужен документ: цены, статусы
Непериодический + подчинённыйРегистратор + номер строкиДокумент при проведенииДанные документа, которые нужны без истории по дате: служебные связи

Как выбрать за 30 секунд:

  1. Нужно знать значение на прошлую дату? Да — периодический. Нет — непериодический.
  2. Изменение значения должно подтверждаться документом (с датой, автором, возможностью отмены)? Да — подчинение регистратору. Нет — независимый.
  3. Значение может меняться несколько раз в день? Да — «В пределах секунды» или «По позиции регистратора». Нет — «В пределах дня» или крупнее.
Какой регистр сведений вам нужен?

Важность измерений и свойства, которые влияют на работу

Измерения задают ключ записи. Ошибка в их составе — главная причина «пропадающих» данных.

Правило ключа

Если измерений слишком мало, разные факты перезаписывают друг друга. Пример: регистр цен с одним измерением «Номенклатура». Розничная и оптовая цена на одну дату — это один ключ, вторая запись затрёт первую. Нужно второе измерение «Вид цены».

Если измерений слишком много, срез вернёт несколько строк там, где ждали одну. Вынесите в реквизиты то, что не участвует в поиске.

Свойства измерений

  • 🔗 «Ведущее» — запись связана с объектом в измерении. Удалили объект — записи регистра удаляются. В форме объекта доступен переход к связанным записям.
  • 🎯 «Основной отбор» — по измерению можно устанавливать отбор в наборе записей и в списке. Ставьте его тем измерениям, по которым реально фильтруют.
  • 🚷 «Запрет незаполненных значений» — платформа не даст записать пустое значение. Полезно для ключевых измерений вроде валюты.
  • ⚡ «Индексировать» — дополнительный индекс для отборов и соединений по измерению.

Свойства регистра

  • 📑 «Основной отбор по периоду» — у периодических регистров позволяет отбирать записи по дате в наборе и в списке.
  • 🚀 Итоги среза последних и среза первых. Платформа может хранить актуальные значения в отдельной таблице. Запрос к СрезПоследних на текущую дату тогда читает готовый результат, а не перебирает историю. Имеет смысл для больших регистров с частыми запросами «на сейчас».

Запись в регистр сведений из встроенного языка

Имена объектов в примерах условные: ЦеныТоваров, ШтрихкодыТоваров. Подставьте свои.

Менеджер записи — одна запись независимого регистра

МенеджерЗаписи = РегистрыСведений.ЦеныТоваров.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период       = НачалоДня(ТекущаяДатаСеанса());
МенеджерЗаписи.Номенклатура = Товар;
МенеджерЗаписи.ВидЦены      = ВидЦены;
МенеджерЗаписи.Цена         = 1250;
МенеджерЗаписи.Записать(); // по умолчанию Замещать = Истина

Записать() с параметром по умолчанию заменяет существующую запись с тем же ключом. Записать(Ложь) при существующей записи вызовет ошибку — так удобно ловить случайные дубли.

Чтобы изменить запись, сначала заполните ключевые поля, вызовите Прочитать(), проверьте Выбран(), затем меняйте ресурсы и записывайте.

Набор записей — много строк сразу

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

Запись = Набор.Добавить();
Запись.Номенклатура = Товар;
Запись.Штрихкод     = "4601234567890";

Набор.Записать(); // заменит все записи, попавшие под отбор

Набор с параметром замещения по умолчанию удаляет всё, что попадает под отбор, и пишет своё содержимое. Если не вызвать Прочитать() перед Добавить(), старые штрихкоды этого товара исчезнут. Если не установить отбор вовсе, перезапишется весь регистр.

Движения документа — подчинённый регистр

Процедура ОбработкаПроведения(Отказ, РежимПроведения)

    Движения.ЦеныТоваров.Записывать = Истина;

    Для Каждого Строка Из Товары Цикл
        Движение = Движения.ЦеныТоваров.Добавить();
        Движение.Период       = Дата;
        Движение.Номенклатура = Строка.Номенклатура;
        Движение.ВидЦены      = ВидЦены;
        Движение.Цена         = Строка.Цена;
    КонецЦикла;

КонецПроцедуры

Записывать = Истина говорит платформе записать движения после обработчика. Старые движения этого регистратора при перепроведении заменяются.

Быстрое чтение без запроса

У менеджера периодического регистра есть ПолучитьПоследнее() и ПолучитьПервое(). Они возвращают структуру ресурсов:

Отбор = Новый Структура("Номенклатура, ВидЦены", Товар, ВидЦены);
Значения = РегистрыСведений.ЦеныТоваров.ПолучитьПоследнее(ТекущаяДатаСеанса(), Отбор);
Цена = Значения.Цена;

Для одного значения это удобно. Для цен по сотне строк используйте запрос: вызов в цикле — это сотня обращений к базе.

Срезы последних и первых в запросах

СрезПоследних(Период, Условие) возвращает по каждому сочетанию измерений последнюю запись с датой не позже указанной. СрезПервых — первую запись не раньше указанной даты.

ВЫБРАТЬ
    Цены.Номенклатура,
    Цены.Цена
ИЗ
    РегистрСведений.ЦеныТоваров.СрезПоследних(
        &ДатаЦен,
        ВидЦены = &ВидЦены
            И Номенклатура В (&СписокТоваров)) КАК Цены

Условие — в параметрах или в ГДЕ

Самая частая ошибка в запросах к срезу — не понимать, когда срабатывает условие. Условие в параметре виртуальной таблицы отбирает записи до вычисления среза. Условие в ГДЕ применяется после.

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

Для отбора по ресурсам и реквизитам результат разный. Пример: у товара измерение одно — «Номенклатура», а валюта цены хранится в реквизите.

  • 1 марта записана цена 1 000 руб.
  • 10 марта — цена 12 USD.
  • Запрос на 15 марта с условием Валюта = &Рубль в параметрах вернёт 1 000 руб. от 1 марта: это последняя рублёвая запись.
  • Тот же запрос с условием в ГДЕ не вернёт товар: последняя запись в долларах, и ГДЕ её отбросит.

Какой вариант правильный, решает задача. «Какая цена действует сейчас и в рублях ли она» — ГДЕ. «Какая была последняя рублёвая цена» — параметр. Ошибка в том, чтобы поставить условие наугад.

Дата среза и время

Если регистр «В пределах секунды», а в &ДатаЦен передана дата без времени (15.03 00:00:00), записи этого дня с более поздним временем в срез не попадут. Передавайте КонецДня(Дата) или точный момент документа.

Срез на дату каждого документа

Виртуальная таблица принимает одну дату. Когда нужны цены на дату каждого документа в выборке, СрезПоследних не подходит. Решение — соединение с самой таблицей регистра через максимальный период:

ВЫБРАТЬ
    Стр.Ссылка КАК Документ,
    Стр.Номенклатура КАК Номенклатура,
    МАКСИМУМ(Цены.Период) КАК Период
ПОМЕСТИТЬ ВТ_Периоды
ИЗ
    Документ.ЗаказКлиента.Товары КАК Стр
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныТоваров КАК Цены
        ПО Цены.Номенклатура = Стр.Номенклатура
            И Цены.ВидЦены = &ВидЦены
            И Цены.Период <= Стр.Ссылка.Дата
ГДЕ
    Стр.Ссылка В (&Документы)
СГРУППИРОВАТЬ ПО
    Стр.Ссылка,
    Стр.Номенклатура
;

ВЫБРАТЬ
    ВТ.Документ,
    ВТ.Номенклатура,
    Цены.Цена
ИЗ
    ВТ_Периоды КАК ВТ
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныТоваров КАК Цены
        ПО Цены.Период = ВТ.Период
            И Цены.Номенклатура = ВТ.Номенклатура
            И Цены.ВидЦены = &ВидЦены

Первый пакет находит для каждой строки дату последней действовавшей цены. Второй достаёт саму цену. Отбор по документам в ГДЕ первого пакета обязателен, иначе СУБД соединит всю табличную часть со всей историей цен.

Для подчинённого регистра этот приём вернёт две строки, если два документа поставили цену на одну и ту же дату. Добавьте в ключ соединения ещё одно поле или исключите такие дубли на этапе проведения.

Работа с физической таблицей

Без виртуальной таблицы к регистру можно обратиться напрямую: РегистрСведений.ЦеныТоваров. Так получают историю целиком — например, отчёт «как менялась цена за квартал» с отбором Период МЕЖДУ &Начало И &Конец.

Частые вопросы

Можно ли сделать непериодический регистр периодическим, если данные уже есть?
Можно. Существующие записи получат пустую дату периода, поэтому после реструктуризации их нужно заполнить обработкой. Иначе срез на любую дату вернёт эти записи как самые ранние.
Почему в независимом регистре нельзя добавить вторую запись на ту же дату?
Период и измерения образуют ключ. На одну дату по одному сочетанию измерений допустима одна запись. Нужна вторая — уменьшите периодичность или добавьте измерение.
Можно ли поменять режим записи с независимого на подчинение регистратору?
Технически да, но существующие записи не имеют регистратора. Их придётся перенести в документы или удалить. Обычно проще создать новый регистр и переписать код.
Есть ли у непериодического регистра срез последних?
Нет. Виртуальные таблицы СрезПоследних и СрезПервых есть только у периодических регистров. У непериодического регистра запрос идёт к основной таблице.
Чем ресурс отличается от реквизита регистра сведений?
По смыслу: ресурс — хранимое значение, реквизит — сопровождающая информация. Ни то ни другое не входит в ключ записи. Методы ПолучитьПоследнее() и ПолучитьПервое() возвращают только ресурсы.