Как найти дату начала последнего непрерывного периода владения в запросе 1С

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет
← На главную

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

Эту задачу в теории баз данных называют проблемой "Gaps and Islands" (Разрывы и острова). Рассмотрим, как эффективно решить ее с помощью механизмов запросов 1С.

Разбор логики задачи на примере

Представим историю перемещения объекта "Тумба":

  1. 01.01.2019 — Иванов (Начало первого владения Ивановым)
  2. 01.02.2019 — Петров (Смена ответственного)
  3. 01.03.2019 — Иванов (Возврат к Иванову, начало нового интервала)
  4. 02.03.2019 — Иванов (Подтверждение или уточнение данных, МОЛ не менялся)

Если мы просто возьмем СрезПоследних, мы получим "Иванов, 02.03.2019". Если мы возьмем минимальную дату по Иванову, мы получим "01.01.2019". Однако правильный ответ согласно условию — 01.03.2019, так как именно в этот день начался последний непрерывный "остров" владения Иванова.

Метод 1: Классический запрос на временных таблицах (для любых версий 8.3)

Этот алгоритм наиболее универсален и работает даже на старых релизах платформы. Мы разделим решение на три логических шага, используя временные таблицы для оптимизации и читаемости кода.

Шаг 1: Получаем текущее состояние. Выясняем, кто является ответственным на текущий момент (или на выбранную дату среза).


// Помещаем текущих МОЛ во временную таблицу
ВЫБРАТЬ
    Рег.ОсновноеСредство КАК ОС,
    Рег.МОЛ КАК ТекущийМОЛ,
    Рег.Период КАК ПериодПоследнейЗаписи
ПОМЕСТИТЬ ВТ_ТекущееСостояние
ИЗ
    РегистрСведений.МестонахождениеОСБухгалтерскийУчет.СрезПоследних(&КонецПериода, ) КАК Рег
ИНДЕКСИРОВАТЬ ПО
    ОС, ТекущийМОЛ
;

Шаг 2: Находим границу "среза". Нам нужно найти максимальную дату, когда ответственным был кто-то другой. Эта дата станет нижней границей для нашего поиска.


ВЫБРАТЬ
    ВТ.ОС,
    МАКСИМУМ(ЕСТЬNULL(История.Период, ДАТАВРЕМЯ(1, 1, 1))) КАК ДатаПоследнейСмены
ПОМЕСТИТЬ ВТ_ГраницаСмены
ИЗ
    ВТ_ТекущееСостояние КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.МестонахождениеОСБухгалтерскийУчет КАК История
        ПО ВТ.ОС = История.ОсновноеСредство
        И ВТ.ТекущийМОЛ <> История.МОЛ  // Ищем записи с ДРУГИМ ответственным
        И ВТ.ПериодПоследнейЗаписи > История.Период
СГРУППИРОВАТЬ ПО
    ВТ.ОС
;

Шаг 3: Вычисляем начало последнего интервала. Теперь мы выбираем минимальную дату из записей регистра, которые относятся к текущему МОЛ и находятся после найденной нами границы.


ВЫБРАТЬ
    ВТ.ОС,
    ВТ.ТекущийМОЛ КАК МОЛ,
    МИНИМУМ(История.Период) КАК ПериодНачала
ИЗ
    ВТ_ТекущееСостояние КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ГраницаСмены КАК Граница
        ПО ВТ.ОС = Граница.ОС
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.МестонахождениеОСБухгалтерскийУчет КАК История
        ПО ВТ.ОС = История.ОсновноеСредство
        И ВТ.ТекущийМОЛ = История.МОЛ
        И История.Период > Граница.ДатаПоследнейСмены
СГРУППИРОВАТЬ ПО
    ВТ.ОС,
    ВТ.ТекущийМОЛ

Метод 2: Использование оконных функций (для платформы 8.3.20 и выше)

Если ваша база работает на современной версии платформы, задачу можно решить значительно элегантнее и быстрее. Мы воспользуемся функцией LAG, чтобы сравнить текущего МОЛ с предыдущим прямо в строке выборки.

Проанализируем этот подход:

  1. Сначала мы нумеруем записи или получаем значение "предыдущего" МОЛ для каждой строки.
  2. Если текущий МОЛ не совпадает с предыдущим — значит, это точка начала нового интервала (флаг смены).
  3. Затем мы просто отфильтровываем все записи, кроме последней группы.

Пример логики с использованием оконных функций:


ВЫБРАТЬ
    Рег.Период,
    Рег.ОсновноеСредство КАК ОС,
    Рег.МОЛ,
    ВЫБОР
        КОГДА Рег.МОЛ = ВЫЧИСЛИТЬВЫРАЖЕНИЕ("МОЛ", , , "Предыдущая", "Предыдущая")
            ТОГДА 0
        ИНАЧЕ 1
    КОНЕЦ КАК ЭтоНачалоОстрова
ИЗ
    РегистрСведений.МестонахождениеОСБухгалтерскийУчет КАК Рег

Дальнейшая фильтрация по максимальному периоду среди записей, где ЭтоНачалоОстрова = 1, даст искомый результат за один проход по таблице.

Важные рекомендации по оптимизации

При работе с большими регистрами (например, сотни тысяч записей перемещения ОС) стоит учитывать следующие нюансы:

Резюме: Чтобы найти начало последнего периода, мы сначала находим, кто владеет объектом сейчас, затем ищем "точку разрыва" (когда владел кто-то другой) и берем самую раннюю дату текущего владельца после этой точки. Этот подход гарантирует точность данных даже при циклической смене ответственных лиц.

← На главную