При изучении внутреннего устройства физических таблиц СУБД в платформе 1С:Предприятие многие разработчики сталкиваются с необычным фактом: системные актуальные итоги по регистрам остатков хранятся вовсе не на максимально возможную дату 31.12.3999 23:59:59, а на начало ноября — 01.11.3999 00:00:00. Разберем подробно, чем обусловлен выбор именно этой даты, как платформа вычисляет остатки в виртуальных таблицах и к каким последствиям могут привести ошибочные движения документов из «будущего».
Для понимания логики платформы проанализируем, как устроен механизм хранения итогов в регистрах накопления остатков и регистрах бухгалтерии:
01.ЧислоМесяца 00:00:00).КонецМесяца + 1 секунда. Если бы точка актуальных итогов была зафиксирована в декабре 3999 года, расчет следующего интервала дал бы дату 01.01.4000. Это привело бы к фатальному системному сбою, так как тип данных Дата в платформе ограничен 4-значным форматом года с верхним пределом 3999.01.11.3999 00:00:00 в качестве заведомо будущей метки. Это исключает переполнение года при любых циклических смещениях по месяцам вперед и назад.Посмотрим на особенности хранения этих данных в реляционных базах. В СУБД Microsoft SQL Server при создании информационной базы часто активируется параметр смещения дат на 2000 лет (для обхода исторического ограничения SQL Server на даты до 1753 года). В этом случае в таблицах итогов AccumRgT и AccRgT дата актуальных итогов физически отображается как 5999-11-01 00:00:00.000.
Выясним, как ведет себя виртуальная таблица Остатки при получении данных через язык запросов в зависимости от переданных параметров:
Неопределено): платформа обращается напрямую к строкам таблицы итогов с датой 01.11.3999. Дополнительные выборки из физической таблицы движений не выполняются. Запрос выполняется максимально быстро, возвращая оперативный остаток на текущий момент времени.01.11.3999 и вычитает из него движения таблицы регистра за весь интервал между требуемым моментом времени и системной точкой актуальности.Таким образом, любое проведение документа (включая документы с датами позже 01.11.3999) физически изменяет запись с датой 01.11.3999, агрегируя абсолютно все движения регистра. Однако если в базе появляются документы с датами позже 1 ноября 3999 года, штатный математический алгоритм восстановления остатков «назад» искажается, а попытка регламентного пересчета итогов падает с ошибкой «Номер года в литерале типа 'Дата' превышает 3999».
В высоконагруженных системах постоянное обновление записи 01.11.3999 в транзакции проведения документов может создавать избыточные блокировки СУБД. Если для регистра накопления не требуется получение мгновенных оперативных остатков (например, при пакетной загрузке исторических данных или проведении документов задним числом), использование актуальных итогов можно временно отключить программно:
// Отключение актуальных итогов для снижения блокировок
РегистрыНакопления.ТоварыНаСкладах.УстановитьИспользованиеТекущихИтогов(Ложь);
// Выполнение массовой обработки документов
// ...
// Включение актуальных итогов обратно
РегистрыНакопления.ТоварыНаСкладах.УстановитьИспользованиеТекущихИтогов(Истина);
Рассмотрим, что происходит внутри базы данных: при установке значения Ложь платформа физически удаляет все записи с датой 01.11.3999 из соответствующей таблицы итогов. При возврате в значение Истина платформа пересчитывает актуальный срез и заново формирует эти записи.
Также важно помнить о проблеме «нулевых» итогов. Если в результате движений остаток товара или взаиморасчетов списывается в ноль, строка с датой 01.11.3999 в таблице итогов не удаляется из СУБД мгновенно, а сохраняется с нулевыми значениями ресурсов. Чтобы физически очистить такие записи и оптимизировать размер базы, необходимо регулярно выполнять регламентный пересчет итогов.
На практике сбой пересчета итогов на дату 3999 года возникает из-за опечаток операторов (например, при вводе года 2099 или 3999 вместо текущего). Разберем по шагам порядок устранения проблемы:
EXCP и SDBL определяем имя физической таблицы СУБД (например, AccumRg12345), вызвавшей исключение, и сопоставляем ее с метаданными конфигурации.
// Принудительный сброс и пересчет итогов по конкретному регистру
МенеджерРегистра = РегистрыНакопления.ТоварыНаСкладах;
МенеджерРегистра.УстановитьИспользованиеИтогов(Ложь);
МенеджерРегистра.УстановитьИспользованиеИтогов(Истина);
МенеджерРегистра.ПересчитатьИтоги();
Такой подход заставляет СУБД полностью пересобрать таблицу итогов, очищая некорректные смещения периодов и возвращая штатную работу виртуальных таблиц остатков.