Разработка и Конфигурация · 10 мин
Как выбрать документ с максимальной датой в запросе 1С
Как в запросе 1С выбрать документ с максимальной датой: МАКСИМУМ, срез последних, УПОРЯДОЧИТЬ ПО с УБЫВ, типичные ошибки и примеры кода.
Задача найти последний по дате документ встречается в разработке на платформе 1С постоянно: нужно определить актуальную цену, последнее состояние заказа, текущего ответственного по договору или последний документ установки цен. У этой задачи есть несколько рабочих решений, и выбор между ними влияет и на корректность результата, и на производительность запроса.
Основная сложность в том, что «максимальная дата» — понятие не всегда однозначное. У нескольких документов может быть одинаковая дата, дата документа может совпадать с датой регистра сведений, а сам факт максимальности нужно проверять в разрезе какого-то объекта: контрагента, номенклатуры, договора. Поэтому простого «взять максимум» часто недостаточно — нужен связанный с ним документ.
Ниже разобраны основные приёмы: агрегатная функция МАКСИМУМ с группировкой, упорядочивание по убыванию с ограничением выборки, виртуальная таблица «СрезПоследних» для регистров сведений, а также типичные ошибки при неоднозначных датах и способы проверить результат.
Как работает агрегатная функция МАКСИМУМ
Базовый способ — использовать агрегатную функцию МАКСИМУМ по полю Дата с группировкой по измерению, в разрезе которого ищется последний документ. Например, для каждого контрагента найти дату его последнего заказа.
ВЫБРАТЬ
ЗаказПокупателя.Контрагент КАК Контрагент,
МАКСИМУМ(ЗаказПокупателя.Дата) КАК ДатаПоследнего
ИЗ
Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
ЗаказПокупателя.Проведен
СГРУППИРОВАТЬ ПО
ЗаказПокупателя.Контрагент
Такой запрос возвращает только дату, но не ссылку на документ. Это принципиальный момент: агрегатная функция «сворачивает» строки, и получить из неё сам документ напрямую нельзя. Для получения ссылки нужен второй шаг — соединение с таблицей документов по найденной дате и измерению.
Учтите, что поле Дата у документа в 1С хранится с точностью до секунды. Два документа, созданные в одну секунду по одному контрагенту, дадут две строки после соединения — и это частый источник «задвоений» в отчётах.
Получение ссылки на документ через соединение
Классический шаблон: сначала во временной таблице или подзапросе находятся максимальные даты, затем выполняется левое соединение с таблицей документов по паре «измерение + дата».
ВЫБРАТЬ
Максимумы.Контрагент КАК Контрагент,
Заказ.Ссылка КАК ПоследнийДокумент,
Заказ.Номер КАК Номер
ИЗ
(ВЫБРАТЬ
ЗаказПокупателя.Контрагент КАК Контрагент,
МАКСИМУМ(ЗаказПокупателя.Дата) КАК ДатаПоследнего
ИЗ
Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
ЗаказПокупателя.Проведен
СГРУППИРОВАТЬ ПО
ЗаказПокупателя.Контрагент) КАК Максимумы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК Заказ
ПО Максимумы.Контрагент = Заказ.Контрагент
И Максимумы.ДатаПоследнего = Заказ.Дата
И Заказ.Проведен
Обратите внимание на три детали. Во-первых, условие «Проведен» должно присутствовать в обеих частях — иначе соединение может подтянуть непроведённый документ с той же датой. Во-вторых, используется левое соединение, чтобы не потерять строки. В-третьих, при совпадении дат до секунды по одному измерению в выборку попадут оба документа.
- 1Определите измерение, в разрезе которого ищется последний документ (контрагент, номенклатура, договор)
- 2Напишите подзапрос с МАКСИМУМ(Дата) и группировкой по измерению, добавьте отбор по проведённым
- 3Соедините результат с таблицей документов по измерению и дате
- 4Проверьте выборку на объекте, где заведомо известен последний документ
- 5Протестируйте случай с двумя документами в одну секунду
Альтернатива: упорядочивание по убыванию с ограничением выборки
Если нужен последний документ по одному конкретному объекту (а не по всем сразу), проще и быстрее использовать упорядочивание с конструкцией ПЕРВЫЕ:
ВЫБРАТЬ ПЕРВЫЕ 1
ЗаказПокупателя.Ссылка КАК Ссылка,
ЗаказПокупателя.Дата КАК Дата
ИЗ
Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
ЗаказПокупателя.Контрагент = &Контрагент
И ЗаказПокупателя.Проведен
УПОРЯДОЧИТЬ ПО
ЗаказПокупателя.Дата УБЫВ
Этот вариант хорош, когда параметр отбора один и известен заранее — например, при проверке в модуле объекта или при программном получении последнего документа по договору. Запрос с ПЕРВЫЕ 1 обычно выполняется быстрее агрегации, потому что СУБД может остановиться после первой подходящей строки при наличии индекса.
Ограничение подхода: он не масштабируется на выборку «последний документ по каждому элементу справочника». Для такой задачи в цикле по контрагентам выполнять отдельные запросы нельзя — это приведёт к сотням обращений к базе. Здесь правильнее агрегация с группировкой.
Сравнение вариантов
Подходит для выборки последних документов сразу по многим объектам. Требует аккуратного соединения и контроля дублей при совпадающих датах.
Оптимально для одного известного объекта. Не подходит для массовой выборки по списку объектов.
Работает только для периодических регистров сведений, но решает задачу в один шаг без соединений.
Срез последних для регистров сведений
Если данные о состоянии хранятся не в документах, а в периодическом регистре сведений (цены, курсы, ответственные лица), платформа предоставляет готовую виртуальную таблицу СрезПоследних. Она возвращает актуальные записи на указанный момент без ручной агрегации.
ВЫБРАТЬ
Срез.Номенклатура КАК Номенклатура,
Срез.Цена КАК Цена,
Срез.Регистратор КАК ДокументУстановки
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Срез
Поле Регистратор в срезе — это и есть документ, которым была установлена последняя запись. Таким образом задача «найти документ с максимальной датой» для регистра сведений решается одной виртуальной таблицей. В параметрах среза первым идёт период, вторым — условие отбора (например, «Номенклатура В (&Список)»).
Важное уточнение: срез последних корректно работает только для регистров с режимом «Подчинение регистратору» или независимых периодических регистров. Если регистр непериодический, виртуальной таблицы среза у него нет.
Проблема одинаковых дат и однозначности результата
Самая коварная ситуация — два документа по одному объекту с датой, совпадающей до секунды. При соединении по дате оба попадут в результат, и отчёт покажет дубли или неверные суммы. Платформа никак не сигнализирует об этом: запрос просто вернёт больше строк, чем ожидалось.
Способы борьбы: добавить в соединение дополнительный разрешающий критерий (например, максимальный номер документа), либо после соединения сгруппировать результат и взять МАКСИМУМ от ссылки — платформа позволяет агрегировать ссылки, и выбор будет детерминированным, хоть и условным. Ещё один вариант — сравнивать не дату, а момент времени документа (поле МоментВремени), который уникален в пределах вида документа.
ВЫБРАТЬ
Максимумы.Контрагент КАК Контрагент,
МАКСИМУМ(Заказ.Ссылка) КАК ПоследнийДокумент
ИЗ
Максимумы КАК Максимумы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК Заказ
ПО Максимумы.Контрагент = Заказ.Контрагент
И Максимумы.ДатаПоследнего = Заказ.Дата
СГРУППИРОВАТЬ ПО
Максимумы.Контрагент
Проверка результата и производительность
Проверять запрос удобнее всего на заведомо известных данных: выберите контрагента, откройте журнал документов с отбором по нему, отсортируйте по дате и сравните последний документ с тем, что вернул запрос. Повторите проверку для объекта без документов — строка должна либо отсутствовать, либо содержать NULL, в зависимости от типа соединения.
По производительности: агрегация по таблице документов с миллионами строк может выполняться заметное время. Сократить объём помогает отбор по периоду (например, искать последний документ только за последний год, если это допустимо по логике) и обязательный индекс по полю измерения. У документов индекс по дате есть по умолчанию, а вот отбор по реквизиту типа Контрагент эффективен только при наличии индекса — проверьте свойство «Индексировать» у реквизита.
Как проверить результат
Совпадает с последним документом в журнале при сортировке по дате
Запрос возвращает другой документ или пустое значение
Одна строка на каждое измерение
Дубли строк по одному измерению — совпали даты до секунды
Строка отсутствует или содержит NULL в поле документа
Подтянулся документ от другого объекта — ошибка в условии соединения
Частые вопросы
Как найти последний документ по всем контрагентам одним запросом?
Используйте подзапрос с МАКСИМУМ(Дата) и группировкой по контрагенту, затем соедините результат с таблицей документов по контрагенту и дате. Не выполняйте отдельный запрос в цикле по каждому контрагенту — это кратно медленнее.
Почему запрос возвращает два документа вместо одного?
Даты документов совпали до секунды, и соединение по дате подтянуло оба. Добавьте группировку с МАКСИМУМ(Ссылка) или сравнивайте уникальный момент времени документа вместо даты.
Что быстрее: МАКСИМУМ или ПЕРВЫЕ 1 с упорядочиванием?
Для одного известного объекта ПЕРВЫЕ 1 с УПОРЯДОЧИТЬ ПО Дата УБЫВ обычно быстрее. Для выборки по множеству объектов единственный корректный вариант — агрегация с группировкой одним запросом.
Как получить последний документ установки цены?
Если цены хранятся в периодическом регистре сведений, используйте виртуальную таблицу СрезПоследних — поле Регистратор в ней и есть документ, установивший актуальную цену. Отдельный запрос по документам не потребуется.
Нужно ли учитывать непроведённые документы?
Зависит от задачи, но в большинстве случаев нет. Добавьте условие Проведен = Истина и в подзапрос агрегации, и в условие соединения — иначе возможно несоответствие между найденной датой и подтянутым документом.
Как найти последний документ за ограниченный период?
Добавьте в условие ГДЕ отбор вида Дата МЕЖДУ &Начало И &Конец в обе части запроса. Это также ускорит выполнение на больших таблицах, так как уменьшит объём сканируемых данных.
Что важно запомнить
Для выборки последнего документа по многим объектам используйте МАКСИМУМ(Дата) с группировкой и соединением по измерению и дате; для одного объекта — ПЕРВЫЕ 1 с упорядочиванием по убыванию; для регистров сведений — СрезПоследних. Всегда дублируйте отбор по проведённым в обеих частях запроса и учитывайте возможность совпадения дат до секунды.