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

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

При разработке отчетов и аналитических выборок часто возникает задача: для каждого заказа покупателя получить дату или сам документ последней отгрузки (например, документа РеализацияТоваровУслуг). Начинающие разработчики нередко пытаются решить эту задачу с помощью конструкции ВЫБРАТЬ ПЕРВЫЕ 1 с сортировкой УПОРЯДОЧИТЬ ПО Дата УБЫВ во вложенном запросе. Однако при соединении такого подзапроса с общим списком заказов результат оказывается пустым или неверным.

Разберем подробно, почему возникает эта ошибка, как работает механизм выборки в 1С и как правильно построить запрос для получения последних событий.

В чем заключается ошибка использования конструкции ВЫБРАТЬ ПЕРВЫЕ 1

Проанализируем ситуацию, с которой сталкивается разработчик. Отдельный запрос с отбором по конкретному заказу возвращает ожидаемый результат:


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

Когда этот запрос переносится во вложенный запрос без условия ГДЕ (в расчете на то, что связь по заказу произойдет в ЛЕВОЕ СОЕДИНЕНИЕ), колонка с датой отгрузки для большинства строк оказывается пустой.

Причина поведения:

  1. Ключевое слово ПЕРВЫЕ N в языке запросов 1С действует глобально на всю результирующую выборку подзапроса, а не внутри каждой группы записей.
  2. В языке запросов 1С отсутствуют оконные функции (такие как ROW_NUMBER() OVER (PARTITION BY ...)) и коррелированные подзапросы (CROSS APPLY).
  3. Вложенный запрос с ВЫБРАТЬ ПЕРВЫЕ 1 возвращает ровно одну строку — самую свежую отгрузку по всей базе данных. Если в основном запросе обрабатываются сотни заказов, эта единственная строка соединится только с тем заказом, к которому она относится, а для всех остальных заказов значение даты будет NULL (пустым).

Правильный способ: агрегатная функция МАКСИМУМ и группировка

Для получения последней даты отгрузки по каждому заказу необходимо использовать группировку СГРУППИРОВАТЬ ПО в сочетании с агрегатной функцией МАКСИМУМ.

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


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

В этом случае СУБД разобьет все строки документов на группы по заказам и для каждого уникального заказа вернет точное максимальное значение даты.

Как получить не только дату, но и ссылку на последний документ

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

Разберем по шагам надежный алгоритм решения этой задачи с использованием временных таблиц:

  1. Формируем временную таблицу максимальных ссылок: Ссылка документа в 1С содержит дату и уникальный идентификатор (GUID). При группировке по заказу функция МАКСИМУМ(Ссылка) выберет самый последний созданный документ, исключая неоднозначность по времени.
  2. Индексируем временную таблицу: Для быстрого соединения в СУБД создаем индекс по полю связи.
  3. Соединяем с основным запросом: В основном запросе соединяем список заказов с временной таблицей и получаем полные данные документа.

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


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

ИНДЕКСИРОВАТЬ ПО
    ЗаказКлиента
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЗаказКлиента.Ссылка КАК Заказ,
    ЗаказКлиента.Номер КАК НомерЗаказа,
    ЗаказКлиента.Дата КАК ДатаЗаказа,
    ВТ_ПоследниеОтгрузки.ПоследняяОтгрузка КАК ДокументОтгрузки,
    ВТ_ПоследниеОтгрузки.ПоследняяОтгрузка.Дата КАК ДатаОтгрузки
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ПоследниеОтгрузки КАК ВТ_ПоследниеОтгрузки
        ПО ЗаказКлиента.Ссылка = ВТ_ПоследниеОтгрузки.ЗаказКлиента

Рекомендации по оптимизации и бизнес-логике

При построении запросов к документам отгрузки обратите внимание на следующие правила разработки в 1С:

  1. Используйте временные таблицы вместо вложенных запросов: Соединение с вложенными запросами мешает оптимизатору СУБД построить эффективный план выполнения и приводит к полному сканированию таблиц (Table Scan). Использование конструкции ПОМЕСТИТЬ с последующим ИНДЕКСИРОВАТЬ ПО обеспечивает максимальное быстродействие.
  2. Проверяйте статус проведения: В таблицах документов всегда отфильтровывайте помеченные на удаление или непроведенные документы с помощью условий Проведен = ИСТИНА и НЕ ПометкаУдаления, чтобы черновики не искажали дату реальной отгрузки.
  3. Используйте регистры при их наличии: Если в конфигурации ведется учет отгрузок по регистрам накопления (например, по регистрам взаиморасчетов или выполнения заказов), получение данных через виртуальные таблицы регистров работает быстрее, чем прямые запросы к таблицам документов.
← На главную