В процессе разработки на платформе 1С:Предприятие одной из самых часто используемых конструкций является виртуальная таблица СрезПоследних регистров сведений. Мы ожидаем, что она вернет нам актуальное состояние данных на определенный момент времени. Однако иногда возникают ситуации, когда вместо одной ожидаемой записи система возвращает две, три или более. Это вызывает недоумение: «Как так? Это же срез последних, он должен вернуть только одно актуальное значение!»
В этой статье мы подробно разберем, почему так происходит, проанализируем внутреннюю механику работы платформы и выясним, как правильно проектировать структуру регистра, чтобы избежать подобных ошибок.
Для начала нам необходимо понять, что именно платформа считает «одной записью» в контексте регистра сведений. Ключевым понятием здесь является уникальность записи, которая определяется набором его измерений (и периодом, если регистр периодический).
Когда мы обращаемся к виртуальной таблице СрезПоследних, система выполняет сложный SQL-запрос к физическим таблицам базы данных. Основная логика этого запроса заключается в том, чтобы найти максимальный период для каждой уникальной комбинации измерений, которая меньше или равна заданной дате. Если мы передаем в запрос условие по одному измерению (например, «Организация»), но в самом регистре определено два измерения («Организация» и «Метод списания»), система будет искать последние записи для каждой пары этих значений.
Рассмотрим пример. Допустим, у нас есть регистр с измерениями Организация и МетодСписания. В базе присутствуют следующие записи:
Если мы запросим срез последних на 01.03.2020, система увидит две разные комбинации измерений. Для комбинации «Организация №1 + По средней» последней является первая запись. Для комбинации «Организация №1 + По ФИФО» последней является вторая запись. В итоге в результат попадут обе строки. Именно это мы и наблюдаем в ситуации, описанной автором темы.
Почему же «Метод списания» оказался в измерениях? Это фундаментальная архитектурная ошибка. Давайте вспомним назначение объектов метаданных:
Если мы помещаем «Метод списания» в измерения, мы сообщаем системе: «Я хочу знать историю того, как использовался метод "По средней" и отдельно историю того, как использовался метод "По ФИФО"». Но в реальности нам нужно знать, какой один метод актуален для организации в данный момент. Следовательно, МетодСписания обязан быть ресурсом.
Как только мы перенесем это поле в ресурсы, ключ уникальности сократится до одного измерения — Организация. Теперь система будет искать только одну последнюю запись для конкретной организации, независимо от того, какое значение ресурса там записано.
Иногда дублирование записей может возникать, если записи были внесены в одну и ту же секунду (для регистров с периодичностью В пределах секунды). Несмотря на то, что это случается редко при ручном вводе, программная запись может создать две записи на один и тот же момент времени с разными значениями измерений.
Если же записи имеют разный период, но обе попадают в срез, — это прямой признак того, что набор измерений в запросе или в структуре регистра шире, чем логический объект, для которого вы хотите получить данные.
Исправление этой ошибки в живой базе требует осторожности. Если мы просто изменим тип поля с «Измерение» на «Ресурс» в конфигураторе, платформа попытается выполнить реструктуризацию таблиц. Однако, если в базе уже есть «дубли» (записи с одинаковой организацией, но разными методами), система выдаст ошибку: «Запись с такими ключевыми полями уже существует». Это произойдет потому, что после удаления лишнего измерения записи станут идентичными по ключевым полям.
Рассмотрим алгоритм правильного исправления по шагам:
ВЫБРАТЬ
Регистр.Организация,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Регистр.МетодСписания) КАК КолВоМетодов
ИЗ
РегистрСведений.УчетнаяПолитика КАК Регистр
СГРУППИРОВАТЬ ПО
Регистр.Организация
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Регистр.МетодСписания) > 1
МетодСписания из раздела Измерения в раздел Ресурсы в конфигураторе 1С.Бывают случаи, когда регистр является частью типовой конфигурации и находится на поддержке, а вносить изменения в структуру запрещено правилами обновления. В такой ситуации проблему «лишних» строк в срезе последних приходится решать на уровне текста запроса.
Мы можем использовать конструкцию ВЫБРАТЬ ПЕРВЫЕ 1, но это сработает корректно только в том случае, если мы четко зададим сортировку. Посмотрим на пример запроса:
ВЫБРАТЬ ПЕРВЫЕ 1
Срез.МетодСписания,
Срез.Период
ИЗ
РегистрСведений.УчетнаяПолитика.СрезПоследних(&ДатаСреза, Организация = &Организация) КАК Срез
УПОРЯДОЧИТЬ ПО
Срез.Период УБЫВ
Важный нюанс: использование ВЫБРАТЬ ПЕРВЫЕ 1 — это борьба с симптомами, а не с болезнью. Такой запрос будет работать медленнее, так как СУБД сначала сформирует полный срез по всем измерениям, а потом будет выполнять сортировку и отсечение лишнего. Если записей в регистре миллионы, это существенно скажется на производительности.
Проанализируем, почему избыточные измерения вредны для системы в целом:
RecordSet.Write() и занимает место на диске.GROUP BY, тем «тяжелее» запрос для оптимизатора.Если ваш СрезПоследних возвращает две записи вместо одной, проверьте следующее:
Помните, что правильное проектирование метаданных — это фундамент стабильной и быстрой работы системы. Измерения должны быть минимально необходимыми для идентификации записи, а все меняющиеся характеристики объекта должны храниться в ресурсах.