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