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