Как в запросе 1С выбрать последний документ по значению реквизита?

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

В практике разработки на платформе 1С часто возникает задача: в документе присутствует определенный реквизит (например, строковый номер, идентификатор внешнего события, номер договора), и одно и то же значение может фигурировать в нескольких документах за разное время. Нам необходимо выбрать наиболее актуальный (последний по дате) документ для каждого такого значения.

Рассмотрим эту проблему подробно, проанализируем ограничения платформы и разберем по шагам готовые решения как для одиночного значения, так и для целого списка значений.

Ограничения языка запросов 1С при решении подобных задач

Прежде чем переходить к написанию кода, выясним причину, почему эта задача в 1С решается сложнее, чем в классическом SQL:

  1. Отсутствие оконных функций: В языке запросов 1С нет аналитических функций вроде ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...), которые позволяют пронумеровать строки внутри группы и мгновенно отфильтровать первую запись за один проход.
  2. Проблема совпадения дат: Поле Дата в платформе хранится с точностью до секунды. Если в пределах одной секунды по одному и тому же значению реквизита создано два документа, простое соединение по максимальной дате вернет обе записи, что приведет к задвоению результата.
  3. Ограничения агрегации: Агрегатная функция МАКСИМУМ не может быть применена к объекту типа МоментВремени в запросе, поэтому сопоставление приходится выполнять по полям Дата и Ссылка.

Вариант 1. Выборка последнего документа для одного известного значения

Если нам требуется найти последний документ только по одному конкретному номеру (например, по номеру «1234»), то задача решается просто. В этом случае мы используем сортировку по убыванию даты и ключевое слово ПЕРВЫЕ 1.

Посмотрим на пример такого запроса:


ВЫБРАТЬ ПЕРВЫЕ 1
    Док.Ссылка КАК ДокументСсылка,
    Док.Дата КАК Дата,
    Док.Номер КАК Номер
ИЗ
    Документ.ПоступлениеТоваров КАК Док
ГДЕ
    Док.Номер = &ИскомыйНомер
УПОРЯДОЧИТЬ ПО
    Док.Дата УБЫВ,
    Док.Ссылка УБЫВ

Обратите внимание: мы добавили упорядочивание не только по полю Дата, но и по полю Ссылка. Это гарантирует детерминированный результат, если вдруг в одну и ту же секунду будут проведены два документа с одинаковым номером.

Однако если номеров много, конструкция ПЕРВЫЕ 1 не подойдет: она ограничит общий результат всего запроса одной строкой, а не вернет по одной строке для каждого номера.

Вариант 2. Пакетный запрос с временной таблицей для множества значений

Когда требуется определить актуальный документ сразу для всех или множества значений реквизита, разберем по шагам классический двухэтапный алгоритм через пакет запросов.

  1. Первый шаг: группируем документы по искомому реквизиту и находим максимальную дату для каждого значения. Результат помещаем во временную таблицу.
  2. Второй шаг: связываем временную таблицу с исходной таблицей документов по значению реквизита и максимальной дате. Чтобы исключить дублирование при совпадении времени, применяем группировку и агрегатную функцию МАКСИМУМ(Ссылка). Платформа 1С однозначно сравнивает ссылки одного типа по их внутреннему идентификатору (UUID).

Рассмотрим текст пакетного запроса:


ВЫБРАТЬ
    Док.Номер КАК НомерРеквизит,
    МАКСИМУМ(Док.Дата) КАК МаксДата
ПОМЕСТИТЬ ВТ_МаксДаты
ИЗ
    Документ.ПоступлениеТоваров КАК Док
СГРУППИРОВАТЬ ПО
    Док.Номер
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.НомерРеквизит КАК НомерРеквизит,
    МАКСИМУМ(Док.Ссылка) КАК ДокументСсылка
ИЗ
    ВТ_МаксДаты КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваров КАК Док
        ПО ВТ.НомерРеквизит = Док.Номер
        И ВТ.МаксДата = Док.Дата
СГРУППИРОВАТЬ ПО
    ВТ.НомерРеквизит

Оптимизация производительности запроса

Проанализируем технические нюансы работы СУБД при выполнении данного алгоритма на больших базах:

  1. Индексация реквизита в метаданных: Для реквизита, по которому производится группировка и условие соединения, в свойствах объекта конфигурации обязательно должно быть установлено свойство «Индексировать» или «Индексировать с доп. упорядочиванием». Без этого СУБД будет выполнять полное сканирование таблицы документов, что приведет к тяжелым блокировкам и медленной работе.
  2. Индексация временной таблицы: Если во временную таблицу попадает более нескольких сотен записей, обязательно добавляем инструкцию индексации перед точкой с запятой первого запроса: ИНДЕКСИРОВАТЬ ПО НомерРеквизит, МаксДата.

Альтернативные архитектурные решения

Если задача получения последнего документа по реквизиту возникает регулярно и критична к скорости выполнения, строить такие запросы к тяжелым таблицам документов не рекомендуется. Разберем более надежные архитектурные подходы:

  1. Использование периодического регистра сведений: Создаем подчиненный регистратору или независимый регистр сведений с периодичностью «По позиции регистратора». В качестве измерения выступает искомый реквизит, а в ресурсе хранится ссылка на документ. В этом случае задача сводится к моментальному получению данных через виртуальную таблицу СрезПоследних.
  2. Использование Системы компоновки данных (СКД): Если выборка строится для отчета, можно настроить группировку по реквизиту, а в детальных записях отсортировать документы по убыванию даты и ограничить количество выводимых строк группировки до 1 без использования сложных временных таблиц.
← На главную