Почему при свертке регистров сведений используется левое соединение со срезом первых?

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

При выполнении свертки информационной базы 1С часто возникает необходимость перенести актуальные значения регистров сведений на дату окончания свертки (ввести начальные остатки или формировать срезы). Однако при анализе кода типовых обработок свертки можно заметить специфическое условие: запрос обращается к виртуальной таблице СрезПоследних, а затем выполняет левое соединение с виртуальной таблицей СрезПервых на начало следующего дня и отсекает записи с помощью условия ТабРегСрезПервых.Период ЕСТЬ NULL.

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

Анализ кода формирования запроса свертки

Проанализируем алгоритм, с помощью которого платформа собирает текст запроса для регистров сведений, подчиненных регистратору:


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|	ЛОЖЬ КАК Активность,
|	&ПериодЗаписи КАК Период,";

Для Каждого ТекИзмерение Из ТекРег.Измерения Цикл
	Запрос.Текст = Запрос.Текст + "
	|	ТабРег." + ТекИзмерение.Имя + ",";
КонецЦикла;

Для Каждого ТекРесурс Из ТекРег.Ресурсы Цикл
	Запрос.Текст = Запрос.Текст + "
	|	ТабРег." + ТекРесурс.Имя + " КАК " + ТекРесурс.Имя + ",";
КонецЦикла;

Для Каждого ТекРеквизит Из ТекРег.Реквизиты Цикл
	Запрос.Текст = Запрос.Текст + "
	|	ТабРег." + ТекРеквизит.Имя + " КАК " + ТекРеквизит.Имя + ",";
КонецЦикла;

Запрос.Текст = Лев(Запрос.Текст, СтрДлина(Запрос.Текст)-1);
Запрос.Текст = Запрос.Текст + "
|ИЗ
|	РегистрСведений." + ТекРег.Имя + ".СрезПоследних(&ДатаОст, ) КАК ТабРег";

Если (НЕ ТекРег.ПериодичностьРегистраСведений = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.ПозицияРегистратора) Тогда
	Запрос.Текст = Запрос.Текст + "
	|	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений." + ТекРег.Имя + ".СрезПервых(&ДатаОст, Период = НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОст, ДЕНЬ, 1), ДЕНЬ)) КАК ТабРегСрезПервых
	|	ПО ИСТИНА";
	
	Для Каждого ТекИзмерение Из ТекРег.Измерения Цикл
		Запрос.Текст = Запрос.Текст + "
		|	И ТабРег." + ТекИзмерение.Имя + " = ТабРегСрезПервых." + ТекИзмерение.Имя;
	КонецЦикла;
	
	Запрос.Текст = Запрос.Текст + "
	|ГДЕ
	|	ТабРегСрезПервых.Период ЕСТЬ NULL";
КонецЕсли;

Запрос.УстановитьПараметр("ДатаОст", ДатаСвертки);
Запрос.УстановитьПараметр("ПериодЗаписи", НачалоДня(ДатаСвертки + 60*60*24));

Разберем параметры запроса. Если дата свертки установлена, например, на 01.07.2016 23:59:59, то параметр &ПериодЗаписи принимает значение 02.07.2016 00:00:00 (начало первого дня нового периода ведения учета).

Назначение левого соединения и отбора по NULL

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

  1. Избыточность данных при смене значений: Если на дата-время 02.07.2016 00:00:00 рабочим документом нового периода уже установлена новая цена или другой показатель, переносить старое значение на эту же дату бессмысленно — оно будет мгновенно перекрыто новой записью.
  2. Конфликт ключей уникальности СУБД: Это ключевая техническая причина. Ключ уникальности записи регистра сведений состоит из всех его измерений и поля Период. Если периодичность регистра сведений зафиксирована (например, В пределах секунды, В пределах дня, В пределах месяца), платформа не позволяет иметь две записи с одинаковыми измерениями в рамках одного периода. При попытке записать сформированные остатки на 02.07.2016 00:00:00, если на эту дату уже существует запись от документа первого дня нового периода, возникнет ошибка СУБД о неуникальности записей.

Почему проверка зависит от свойства Периодичность?

Обратим внимание на условие проверки свойств регистра:

Если (НЕ ТекРег.ПериодичностьРегистраСведений = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.ПозицияРегистратора)

Рассмотрим различие двух вариантов настройки периодичности регистров сведений, подчиненных регистратору:

  1. Периодичность «По позиции регистратора»: В состав ключа уникальности записи, помимо измерений и периода, входит регистратор (ссылка на документ) и его позиция на временной оси. Это означает, что в рамках одной секунды на дату 02.07.2016 00:00:00 в базе может существовать несколько записей с одинаковым набором измерений, сделанных разными документами. Ошибки уникальности не возникнет, поэтому для таких регистров выбираются все записи среза последних без дополнительных фильтров.
  2. Фиксированная периодичность («Секунда», «День», «Месяц» и др.): В данном случае документ-регистратор не входит в ключ уникальности. Запись на границе периода может быть только одна для уникального набора измерений. Именно поэтому для всех таких регистров выполняется левое соединение с первыми записями нового периода, и если запись на начало следующего дня уже существует (ТабРегСрезПервых.Период НЕ ЕСТЬ NULL), начальный остаток по данным измерениям не формируется.

Сводка правил для разработчика

При самостоятельной разработке алгоритмов свертки или переноса срезов сведений следует придерживаться следующих правил:

1. Фиксация входящих остатков на конец дня: Чтобы избежать совпадения времени сверточных записей и новых первичных документов, документы ввода остатков чаще всего записывают на время 23:59:59 дня, предшествующего началу нового периода (например, 01.07.2016 23:59:59).

2. Учет структуры ключа записи: Перед выполнением групповой записи через наборы записей с установленным свойством ОбменДанных.Загрузка = Истина всегда проверяйте режим записи и периодичность регистра. Свойство Загрузка = Истина не отменяет физический контроль уникальности ключа на уровне СУБД.

← На главную