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

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

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

Поскольку в языке запросов 1С отсутствуют стандартные аналитические оконные функции (такие как ROW_NUMBER() OVER (...)), прямолинейно отфильтровать строки «на лету» невозможно. Разберем по шагам, как построить эффективный и детерминированный запрос для решения этой задачи, а также выясним, с какими типичными ошибками сталкиваются разработчики.

Почему не работает конструкция МАКСИМУМ(МоментВремени)

Довольно часто у разработчиков возникает соблазн сгруппировать таблицу документов по дате и применить агрегатную функцию к полю МоментВремени. Однако при попытке выполнить запрос вида МАКСИМУМ(Док.МоментВремени) платформа выдает синтаксическую ошибку «Неверные параметры в функции...».

Проанализируем причину такого поведения: тип МоментВремени в платформе 1С является составным объектом, состоящим из двух составляющих — даты со временем и ссылки на документ. На уровне СУБД это не единое скалярное значение. Агрегатные функции языка запросов 1С (МАКСИМУМ, МИНИМУМ, СУММА) предназначены для работы только с простыми базовыми типами: числами, строками, датами и ссылками. Применить агрегацию к объекту МоментВремени невозможно, поэтому задачу необходимо решать через стандартные поля документа.

Разбор типовой ситуации: метод временных таблиц

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

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

Посмотрим на практический пример реализации пакетного запроса:


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

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Итог.ПериодДень КАК День,
    Док.Ссылка КАК Документ,
    Док.Номер КАК Номер,
    Док.Дата КАК Дата,
    Док.СуммаДокумента КАК Сумма
ИЗ
    ВТ_ИтоговыеДокументы КАК Итог
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Док
        ПО Итог.Ссылка = Док.Ссылка

Альтернативный вариант: отбор по номеру документа

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

Рассмотрим, как меняется логика соединения во втором пакете запроса:


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

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

Рекомендации по оптимизации запроса

Проанализируем производительность при работе с большими объемами данных. Если выборка строится за длительный интервал времени (несколько месяцев или лет), необходимо учитывать следующие моменты:

  1. Индексация временных таблиц. Для ускорения соединений всегда индексируйте временные таблицы по ключевым полям связи. В первом пакете запроса добавьте конструкцию: ИНДЕКСИРОВАТЬ ПО ПериодДень, МаксДата. Это позволит СУБД применять эффективные алгоритмы соединения.
  2. Фильтрация по периоду на первом шаге. Обязательно ограничивайте выборку в секции ГДЕ уже в самом первом запросе пакета, чтобы во временную таблицу попадали только необходимые дни, а не вся таблица документов за всю историю базы.
  3. Использование директивы АВТОУПОРЯДОЧИВАНИЕ. Помните, что при наличии инструкции АВТОУПОРЯДОЧИВАНИЕ платформа сортирует ссылочные поля по внутренним правилам (хронологически по дате и ссылке). Без этой директивы порядок ссылок в результирующей таблице опирается на внутренний бинарный идентификатор объекта (UUID).
← На главную