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