При формировании витрин товаров, прайс-листов или печатных форм периодически возникает необходимость выводить позиции не привычными монолитными блоками по категориям, а ротируемым списком. Рассмотрим задачу: у нас есть справочник номенклатуры, разделенный на произвольные группы (например, Группа А, Группа Б, ..., Группа Х). Требуется выстроить список так, чтобы сначала шел первый элемент из группы А, затем первый из группы Б, ..., первый из группы Х, а следом — вторые и третьи элементы всех групп по порядку.
Разберем по шагам, как реализовать подобную группировку и сортировку средствами платформы 1С несколькими способами.
Рассмотрим наименее ресурсоемкий и самый универсальный подход — обработку предварительно полученных данных прямо на сервере с помощью объекта ТаблицаЗначений.
Проанализируем ключевую идею метода: нам необходимо вычислить так называемый «вес» (или порядковый номер) каждого товара внутри его родной группы, а затем выполнить составную сортировку.
ТаблицаЗначений с нужными колонками: Группа, Номенклатура и Вес (числовое поле).Группа, чтобы элементы одной категории располагались последовательно.Вес выполняем метод сортировки по двум колонкам: ТЗ.Сортировать("Вес, Группа").Посмотрим на пример реализации данного алгоритма на языке 1С:
// Создаем структуру таблицы значений
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Группа");
ТЗ.Колонки.Добавить("Номенклатура");
ТЗ.Колонки.Добавить("Вес", Новый ОписаниеТипов("Число"));
// Заполняем ТЗ исходными данными (из запроса или справочника)
// ...
// Предварительно упорядочиваем по группам для корректного подсчета
ТЗ.Сортировать("Группа");
ТекущаяГруппа = Неопределено;
СчетчикВеса = 0;
Для Каждого Срока Из ТЗ Цикл
Если Срока.Группа <> ТекущаяГруппа Тогда
ТекущаяГруппа = Срока.Группа;
СчетчикВеса = 1;
Иначе
СчетчикВеса = СчетчикВеса + 1;
КонецЕсли;
Срока.Вес = СчетчикВеса;
КонецЦикла;
// Главный шаг: сортируем по весу, а затем по группе
ТЗ.Сортировать("Вес, Группа");
Важное замечание: сортировать обязательно нужно именно по комбинации "Вес, Группа". Если отсортировать только по полю Вес, платформа 1С не гарантирует порядок следования групп между собой, и порядок элементов внутри одного блока веса может хаотично меняться.
Если требуется обойтись сортировкой по одной-единственной колонке, разберем математический вариант расчета составного числового индекса. Порядок вычисляется по формуле:
ИндексСортировки = (НомерЭлементаВГруппе * ОбщееКоличествоГрупп) + НомерГруппы
При таком подходе каждый товар получает уникальный числовой приоритет, который автоматически выстраивает все элементы в правильную цепочку ротации при стандартной сортировке по возрастанию.
Выясним, как решить задачу на уровне запроса 1С. Поскольку в языке запросов 1С отсутствуют стандартные оконные функции (такие как ROW_NUMBER() OVER (...) из T-SQL или PostgreSQL), нумерацию элементов внутри группы можно получить с помощью левого самосоединения таблицы с агрегацией.
Рассмотрим пример запроса:
ВЫБРАТЬ
Т1.Родитель КАК Группа,
Т1.Ссылка КАК Номенклатура,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.Ссылка) + 1 КАК Вес
ИЗ
Справочник.Номенклатура КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т2
ПО Т1.Родитель = Т2.Родитель
И Т1.Ссылка > Т2.Ссылка
ГДЕ
НЕ Т1.ЭтоГруппа
СГРУППИРОВАТЬ ПО
Т1.Родитель,
Т1.Ссылка
УПОРЯДОЧИТЬ ПО
Вес,
Группа,
Номенклатура
Обратите внимание на производительность: условие соединения Т1.Ссылка > Т2.Ссылка имеет квадратичную вычислительную сложность O(N²). На небольших справочниках этот запрос отработает мгновенно, но если в группах содержатся десятки тысяч позиций, запрос вызовет высокую нагрузку на сервер БД. На больших объемах данных предпочтительнее использовать программный обход в ТаблицаЗначений на сервере.
Если задача заключается в подготовке отчета, менять код запроса не требуется. В Системе компоновки данных (СКД) предусмотрено системное поле СистемныеПоля.НомерПоПорядкуВГруппе.
Настроив группировку по данному системному полю на верхнем уровне и добавив подгруппу по родительской категории, вы автоматически получите необходимый чередующийся порядок вывода элементов на печать или в форму отчета.