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