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

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

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

Метод 1. Использование вложенного запроса с двойной сортировкой

Самый популярный и очевидный способ — это выбрать два последних документа, а затем из этого набора взять первый, изменив порядок сортировки на противоположный. Проанализируем этот механизм. Сначала мы создаем внутренний запрос, который отбирает ПЕРВЫЕ 2 документа, отсортированных по убыванию даты. Затем внешний запрос выбирает из них ПЕРВЫЕ 1, но уже по возрастанию.

Разберем пример кода для получения предпоследнего документа типа Встреча:


ВЫБРАТЬ ПЕРВЫЕ 1
    Влож.Ссылка КАК ПредпоследнийДокумент
ИЗ
    (ВЫБРАТЬ ПЕРВЫЕ 2
        Документ.Встреча.Ссылка КАК Ссылка,
        Документ.Встреча.МоментВремени КАК МоментВремени
    ИЗ
        Документ.Встреча КАК Документ
    УПОРЯДОЧИТЬ ПО
        МоментВремени УБЫВ) КАК Влож
УПОРЯДОЧИТЬ ПО
    Влож.МоментВремени ВОЗР

Важный нюанс: В этом решении мы используем поле МоментВремени вместо обычной даты. Это критически важно, так как в 1С в одну и ту же секунду может быть введено несколько документов. Сортировка только по дате может вернуть непредсказуемый результат, тогда как МоментВремени гарантирует строгую последовательность.

Метод 2. Исключение последнего документа

Если нам нужно найти предпоследний документ относительно какого-то конкретного (например, текущего), мы можем упростить конструкцию. Достаточно выбрать последний документ, дата которого строго меньше даты текущего документа. Выясним причину, почему это удобно: нам не нужно строить сложные вложенные конструкции «двое из одного», мы просто берем ПЕРВЫЕ 1 по убыванию с фильтром.

Рассмотрим пример, где нам нужно найти документ, предшествующий конкретной ссылке:


ВЫБРАТЬ ПЕРВЫЕ 1
    Документ.Ссылка КАК Ссылка
ИЗ
    Документ.Продажи КАК Документ
ГДЕ
    Документ.МоментВремени < &ТекущийМоментВремени
УПОРЯДОЧИТЬ ПО
    Документ.МоментВремени УБЫВ

Метод 3. Получение предпоследних документов для списка объектов

Ситуация усложняется, если нам нужно получить предпоследние документы не в целом по базе, а для каждого контрагента или договора отдельно. В языке запросов 1С отсутствует прямая поддержка функции OFFSET (пропустить N строк), поэтому мы будем использовать условие В (ВЫБРАТЬ ПЕРВЫЕ...).

Проанализируем пример получения двух последних документов по каждому контрагенту:


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

Этот запрос вернет по две строки для каждого контрагента. Далее в коде 1С или с помощью дополнительной группировки можно вычислить разницу показателей. Однако помните, что использование вложенных запросов в условии ГДЕ может существенно замедлить работу базы при больших объемах данных.

Метод 4. Оптимизация через временные таблицы

Для повышения производительности, особенно когда требуется рассчитать разницу сумм между последним и предпоследним документами, рекомендуется использовать временные таблицы. Посмотрим на алгоритм:

  1. Создаем временную таблицу ВТ_Последние, куда помещаем максимальные моменты времени документов для каждого объекта.
  2. Создаем вторую временную таблицу ВТ_ВсеКромеПоследних, куда выбираем документы, исключая те, что попали в первую таблицу.
  3. Из второй таблицы выбираем максимальные значения — это и будут наши предпоследние документы.

Такой подход позволяет СУБД эффективнее использовать индексы по дате и ссылке.

Проблема пустых значений (NULL) и единственный документ

Рассмотрим ситуацию, когда в базе по конкретному контрагенту есть всего один документ. В этом случае алгоритм поиска предпоследнего документа вернет пустой результат. При расчете разницы сумм (СуммаПоследнего - СуммаПредпоследнего) возникнет NULL, что может привести к ошибке в отчете.

Чтобы этого избежать, всегда используйте функцию ЕСТЬNULL. Проанализируем правильный подход к вычислению разницы:


ВЫБРАТЬ
    ЕСТЬNULL(ДокПоследний.Сумма, 0) - ЕСТЬNULL(ДокПредпоследний.Сумма, 0) КАК Разница
ИЗ
    ...

Это обеспечит корректную работу алгоритма, даже если истории операций еще недостаточно для полноценного сравнения.

Использование регистров как альтернатива

Если задача стоит в получении данных о суммах или остатках, иногда быстрее обратиться не к таблицам документов, а к регистрам накопления. Виртуальные таблицы оборотов с периодичностью Регистратор уже содержат упорядоченные данные. Выясним преимущество: работа с регистрами зачастую быстрее, так как платформа 1С оптимизирует запросы к ним на уровне SQL-индексов. Для получения предпоследнего движения можно использовать те же механизмы вложенных запросов, примененные к виртуальной таблице Обороты.

Подведем итог: Для надежного получения предпоследнего документа всегда используйте МоментВремени для сортировки и конструкцию вложенного запроса с разворотом порядка (УБЫВ во вложенном, ВОЗР во внешнем). Это гарантирует получение верной ссылки даже при совпадении времени документов до секунды.

← На главную