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