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