В практической разработке на платформе 1С:Предприятие регулярно возникает задача: из массива документов или записей регистра получить строго одну последнюю запись на каждую календарную дату. Например, это требуется при анализе закрывающих операций дня, получении актуального статуса или вычислении итоговых остатков без использования периодических итогов.
Поскольку в языке запросов 1С отсутствуют стандартные аналитические оконные функции (такие как ROW_NUMBER() OVER (...)), прямолинейно отфильтровать строки «на лету» невозможно. Разберем по шагам, как построить эффективный и детерминированный запрос для решения этой задачи, а также выясним, с какими типичными ошибками сталкиваются разработчики.
Довольно часто у разработчиков возникает соблазн сгруппировать таблицу документов по дате и применить агрегатную функцию к полю МоментВремени. Однако при попытке выполнить запрос вида МАКСИМУМ(Док.МоментВремени) платформа выдает синтаксическую ошибку «Неверные параметры в функции...».
Проанализируем причину такого поведения: тип МоментВремени в платформе 1С является составным объектом, состоящим из двух составляющих — даты со временем и ссылки на документ. На уровне СУБД это не единое скалярное значение. Агрегатные функции языка запросов 1С (МАКСИМУМ, МИНИМУМ, СУММА) предназначены для работы только с простыми базовыми типами: числами, строками, датами и ссылками. Применить агрегацию к объекту МоментВремени невозможно, поэтому задачу необходимо решать через стандартные поля документа.
Рассмотрим классический и наиболее надежный способ решения через пакетный запрос с временными таблицами. Этот подход разделяет алгоритм на понятные этапы и гарантирует корректность выборки даже в высоконагруженных базах.
НАЧАЛОПЕРИОДА и вычисляем максимальную дату и время за эти сутки.МАКСИМУМ(Ссылка). Ссылочный тип в 1С поддерживает агрегатную функцию МАКСИМУМ, что позволяет получить ровно один уникальный документ на каждую дату.Посмотрим на практический пример реализации пакетного запроса:
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ) КАК ПериодДень,
МАКСИМУМ(Док.Дата) КАК МаксДата
ПОМЕСТИТЬ ВТ_МаксДаты
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
СГРУППИРОВАТЬ ПО
НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.ПериодДень КАК ПериодДень,
МАКСИМУМ(Док.Ссылка) КАК Ссылка
ПОМЕСТИТЬ ВТ_ИтоговыеДокументы
ИЗ
ВТ_МаксДаты КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Док
ПО НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ) = ВТ.ПериодДень
И Док.Дата = ВТ.МаксДата
СГРУППИРОВАТЬ ПО
ВТ.ПериодДень
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Итог.ПериодДень КАК День,
Док.Ссылка КАК Документ,
Док.Номер КАК Номер,
Док.Дата КАК Дата,
Док.СуммаДокумента КАК Сумма
ИЗ
ВТ_ИтоговыеДокументы КАК Итог
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Док
ПО Итог.Ссылка = Док.Ссылка
В некоторых бизнес-процессах при совпадении времени проведения документов до секунды решающим фактором является не системный идентификатор ссылки, а порядковый номер документа. В таком случае на втором шаге вместо функции МАКСИМУМ(Ссылка) мы можем ориентироваться на максимальный номер.
Рассмотрим, как меняется логика соединения во втором пакете запроса:
ВЫБРАТЬ
ВТ.ПериодДень КАК ПериодДень,
МАКСИМУМ(Док.Номер) КАК МаксНомер
ПОМЕСТИТЬ ВТ_МаксНомера
ИЗ
ВТ_МаксДаты КАК ВТ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Док
ПО НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ) = ВТ.ПериодДень
И Док.Дата = ВТ.МаксДата
СГРУППИРОВАТЬ ПО
ВТ.ПериодДень
;
Важный нюанс: при сравнении номеров документов строкового типа строковая сортировка платформы учитывает лидирующие нули. Если нумерация в базе ведется с переменной длиной или содержит нестандартные префиксы, сравнение строк может выдать неожиданный результат, поэтому наиболее безопасным вариантом для гарантии единичности строки остается МАКСИМУМ(Ссылка).
Проанализируем производительность при работе с большими объемами данных. Если выборка строится за длительный интервал времени (несколько месяцев или лет), необходимо учитывать следующие моменты:
ИНДЕКСИРОВАТЬ ПО ПериодДень, МаксДата. Это позволит СУБД применять эффективные алгоритмы соединения.ГДЕ уже в самом первом запросе пакета, чтобы во временную таблицу попадали только необходимые дни, а не вся таблица документов за всю историю базы.АВТОУПОРЯДОЧИВАНИЕ платформа сортирует ссылочные поля по внутренним правилам (хронологически по дате и ссылке). Без этой директивы порядок ссылок в результирующей таблице опирается на внутренний бинарный идентификатор объекта (UUID).