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

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

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

Анализ исходной проблемы и неверного запроса

Рассмотрим первоначальный запрос, с которым возникли сложности. Задача — получить для каждого товара дату его последней продажи за определенный период.

Автор темы предложил следующий код:


ВЫБРАТЬ
    Продажи.Регистратор.Дата КАК РегистраторДата,
    Продажи.Товар.Наименование КАК ТоварНаименование,
    Продажи.Склад.Наименование КАК СкладНаименование
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Регистратор.Дата МЕЖДУ ДАТАВРЕМЯ(2022, 5, 1) И ДАТАВРЕМЯ(2022, 10, 15, 23, 59, 59)
СГРУППИРОВАТЬ ПО
    Продажи.Товар.Наименование,
    Продажи.Регистратор.Дата,
    Продажи.Склад.Наименование
УПОРЯДОЧИТЬ ПО
    Продажи.Регистратор.Дата УБЫВ

В чем здесь ошибка? Проблема кроется в конструкции СГРУППИРОВАТЬ ПО. Включая в группировку поле Продажи.Регистратор.Дата, мы говорим системе: "Создай уникальную строку для каждой комбинации 'Товар + Склад + Дата'". Если товар продавался 5 раз в разные дни, мы получим 5 строк в результате. Конструкция УПОРЯДОЧИТЬ ПО ... УБЫВ лишь отсортирует этот результат по убыванию даты, но не отфильтрует "старые" продажи. В итоге мы получим список всех продаж, а не только последних.

Правильное решение: используем МАКСИМУМ() и СГРУППИРОВАТЬ ПО

Для решения нашей задачи нужно использовать агрегатную функцию. Такие функции выполняют вычисления на наборе значений и возвращают одиночное значение. Для поиска последней (максимальной) даты идеально подходит функция МАКСИМУМ().

Ключевой момент — правильно определить, по каким полям мы хотим сгруппировать данные. Нам нужна последняя дата для каждой уникальной пары "Товар + Склад". Именно эти поля и должны быть в секции СГРУППИРОВАТЬ ПО.

Правильный запрос будет выглядеть так:


ВЫБРАТЬ
    МАКСИМУМ(Продажи.Период) КАК ДатаПоследнейПродажи,
    Продажи.Товар.Наименование КАК ТоварНаименование,
    Продажи.Склад.Наименование КАК СкладНаименование
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Период МЕЖДУ ДАТАВРЕМЯ(2022, 5, 1) И ДАТАВРЕМЯ(2022, 10, 15, 23, 59, 59)
СГРУППИРОВАТЬ ПО
    Продажи.Товар.Наименование,
    Продажи.Склад.Наименование

Разберем по шагам, что делает этот запрос:

  1. ИЗ РегистрНакопления.Продажи — выбирает все записи из регистра "Продажи".
  2. ГДЕ Продажи.Период МЕЖДУ ... — фильтрует записи, оставляя только те, что входят в нужный нам временной интервал.
  3. СГРУППИРОВАТЬ ПО Продажи.Товар.Наименование, Продажи.Склад.Наименование — это сердце запроса. Система берет все отфильтрованные записи и "склеивает" их в группы. Каждая группа представляет собой уникальную пару значений полей "Товар" и "Склад".
  4. ВЫБРАТЬ МАКСИМУМ(Продажи.Период) ... — для каждой созданной группы система находит максимальное значение в поле Период. В итоге для каждой пары "Товар + Склад" будет выведена только одна, самая последняя дата продажи.

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

Важные технические нюансы и частые ошибки

1. `Период` или `Регистратор.Дата`?

Вы могли заметить, что в исправленном запросе мы использовали поле Продажи.Период вместо Продажи.Регистратор.Дата. В чем разница?

Вывод: С точки зрения производительности и правильности архитектуры, для получения даты записи в регистре всегда лучше использовать поле Период.

2. Ошибка с функцией `МИНИМУМ()`

В процессе обсуждения автор по ошибке попробовал использовать функцию МИНИМУМ() для поиска последней даты. Это логическая ошибка. Функция МИНИМУМ() вернет самую раннюю дату продажи из выбранного периода, что является противоположностью поставленной задаче.


-- НЕПРАВИЛЬНО для поиска последней даты
ВЫБРАТЬ
    МИНИМУМ(Продажи.Период) КАК ДатаПервойПродажи,
    ...

Как получить не только дату, но и другие данные последней продажи?

Представленный выше запрос отлично находит последнюю дату. Но что, если нам нужен еще и номер документа последней продажи или количество проданного товара именно в той последней операции? Если мы просто добавим поле Регистратор или Количество в секцию ВЫБРАТЬ, 1С потребует добавить их и в СГРУППИРОВАТЬ ПО, что вернет нас к исходной проблеме.

Для таких задач используется более сложная конструкция с вложенным запросом или временной таблицей. Рассмотрим этот подход.

  1. Шаг 1: Создаем внутренний запрос, который находит последние даты продаж для каждой группы (тот самый запрос, что мы уже написали).
  2. Шаг 2: Во внешнем запросе соединяем исходную таблицу регистра (РегистрНакопления.Продажи) с результатом внутреннего запроса. Соединение выполняется по ключевым полям группировки (Товар, Склад) и по найденной максимальной дате.

Посмотрим на пример такого запроса:


ВЫБРАТЬ
    Продажи.Регистратор,
    Продажи.Товар.Наименование,
    Продажи.Склад.Наименование,
    Продажи.Период КАК ДатаПоследнейПродажи,
    Продажи.Количество
ИЗ
    РегистрНакопления.Продажи КАК Продажи
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
            МАКСИМУМ(Т.Период) КАК ПоследняяДата,
            Т.Товар КАК Товар,
            Т.Склад КАК Склад
        ИЗ
            РегистрНакопления.Продажи КАК Т
        ГДЕ
            Т.Период МЕЖДУ &НачалоПериода И &КонецПериода
        СГРУППИРОВАТЬ ПО
            Т.Товар,
            Т.Склад) КАК ПоследниеДаты
        ПО Продажи.Товар = ПоследниеДаты.Товар
            И Продажи.Склад = ПоследниеДаты.Склад
            И Продажи.Период = ПоследниеДаты.ПоследняяДата

Этот запрос сначала находит уникальные последние даты для каждой пары "Товар-Склад", а затем, используя эти данные, "вытаскивает" из основной таблицы все поля (включая Регистратор и Количество), которые соответствуют именно этим последним продажам. Это мощный и универсальный прием, который часто используется в сложных отчетах.

← На главную