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