В практике разработки на платформе 1С:Предприятие часто возникает задача анализа истории изменений в периодических регистрах сведений. Типичный пример: необходимо определить, с какой даты за основным средством (ОС) закреплен текущий материально-ответственный лицу (МОЛ). Сложность заключается в том, что один и тот же человек мог владеть этим имуществом в разные годы, и нам нужно найти не самую первую запись по нему вообще, а дату начала именно последнего непрерывного интервала.
Эту задачу в теории баз данных называют проблемой "Gaps and Islands" (Разрывы и острова). Рассмотрим, как эффективно решить ее с помощью механизмов запросов 1С.
Представим историю перемещения объекта "Тумба":
Если мы просто возьмем СрезПоследних, мы получим "Иванов, 02.03.2019". Если мы возьмем минимальную дату по Иванову, мы получим "01.01.2019". Однако правильный ответ согласно условию — 01.03.2019, так как именно в этот день начался последний непрерывный "остров" владения Иванова.
Этот алгоритм наиболее универсален и работает даже на старых релизах платформы. Мы разделим решение на три логических шага, используя временные таблицы для оптимизации и читаемости кода.
Шаг 1: Получаем текущее состояние. Выясняем, кто является ответственным на текущий момент (или на выбранную дату среза).
// Помещаем текущих МОЛ во временную таблицу
ВЫБРАТЬ
Рег.ОсновноеСредство КАК ОС,
Рег.МОЛ КАК ТекущийМОЛ,
Рег.Период КАК ПериодПоследнейЗаписи
ПОМЕСТИТЬ ВТ_ТекущееСостояние
ИЗ
РегистрСведений.МестонахождениеОСБухгалтерскийУчет.СрезПоследних(&КонецПериода, ) КАК Рег
ИНДЕКСИРОВАТЬ ПО
ОС, ТекущийМОЛ
;
Шаг 2: Находим границу "среза". Нам нужно найти максимальную дату, когда ответственным был кто-то другой. Эта дата станет нижней границей для нашего поиска.
ВЫБРАТЬ
ВТ.ОС,
МАКСИМУМ(ЕСТЬNULL(История.Период, ДАТАВРЕМЯ(1, 1, 1))) КАК ДатаПоследнейСмены
ПОМЕСТИТЬ ВТ_ГраницаСмены
ИЗ
ВТ_ТекущееСостояние КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МестонахождениеОСБухгалтерскийУчет КАК История
ПО ВТ.ОС = История.ОсновноеСредство
И ВТ.ТекущийМОЛ <> История.МОЛ // Ищем записи с ДРУГИМ ответственным
И ВТ.ПериодПоследнейЗаписи > История.Период
СГРУППИРОВАТЬ ПО
ВТ.ОС
;
Шаг 3: Вычисляем начало последнего интервала. Теперь мы выбираем минимальную дату из записей регистра, которые относятся к текущему МОЛ и находятся после найденной нами границы.
ВЫБРАТЬ
ВТ.ОС,
ВТ.ТекущийМОЛ КАК МОЛ,
МИНИМУМ(История.Период) КАК ПериодНачала
ИЗ
ВТ_ТекущееСостояние КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ГраницаСмены КАК Граница
ПО ВТ.ОС = Граница.ОС
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.МестонахождениеОСБухгалтерскийУчет КАК История
ПО ВТ.ОС = История.ОсновноеСредство
И ВТ.ТекущийМОЛ = История.МОЛ
И История.Период > Граница.ДатаПоследнейСмены
СГРУППИРОВАТЬ ПО
ВТ.ОС,
ВТ.ТекущийМОЛ
Если ваша база работает на современной версии платформы, задачу можно решить значительно элегантнее и быстрее. Мы воспользуемся функцией LAG, чтобы сравнить текущего МОЛ с предыдущим прямо в строке выборки.
Проанализируем этот подход:
Пример логики с использованием оконных функций:
ВЫБРАТЬ
Рег.Период,
Рег.ОсновноеСредство КАК ОС,
Рег.МОЛ,
ВЫБОР
КОГДА Рег.МОЛ = ВЫЧИСЛИТЬВЫРАЖЕНИЕ("МОЛ", , , "Предыдущая", "Предыдущая")
ТОГДА 0
ИНАЧЕ 1
КОНЕЦ КАК ЭтоНачалоОстрова
ИЗ
РегистрСведений.МестонахождениеОСБухгалтерскийУчет КАК Рег
Дальнейшая фильтрация по максимальному периоду среди записей, где ЭтоНачалоОстрова = 1, даст искомый результат за один проход по таблице.
При работе с большими регистрами (например, сотни тысяч записей перемещения ОС) стоит учитывать следующие нюансы:
ОС, МОЛ).<> в соединениях. На уровне SQL-сервера это может привести к полному сканированию таблицы (Scan) вместо поиска по индексу (Seek). Если производительность низкая, рассмотрите возможность ограничения выборки по времени (например, анализировать историю только за последние 5 лет).Период недостаточно. В таких случаях в соединениях и сравнениях следует использовать МоментВремени, чтобы гарантировать правильную последовательность истории.СрезПоследних оправдано только на первом этапе для определения текущего ответственного. Всю остальную историю придется получать из физической таблицы регистра.Резюме: Чтобы найти начало последнего периода, мы сначала находим, кто владеет объектом сейчас, затем ищем "точку разрыва" (когда владел кто-то другой) и берем самую раннюю дату текущего владельца после этой точки. Этот подход гарантирует точность данных даже при циклической смене ответственных лиц.