В практике разработчика 1С иногда возникают задачи по нестандартному упорядочиванию данных. Например, когда необходимо вывести записи «в два столбца» или особым образом чередовать строки: сначала первую запись из первой половины списка, затем первую запись из второй половины, и так далее (1, 51, 2, 52...). Рассмотрим подробно, как реализовать такой алгоритм с помощью запросов, системы компоновки данных (СКД) и программного кода.
Проанализируем наиболее эффективный способ, который не требует сложных соединений таблиц и основан на вычислении «веса» для каждой строки. Идея заключается в том, чтобы присвоить каждой записи числовой индекс, по которому СУБД выполнит итоговую сортировку. Предположим, у нас есть общее количество записей N, а середина списка — это M = N / 2.
Разберем логику вычисления веса. Для записей из первой половины (от 1 до M) весом будет их порядковый номер. Для записей из второй половины (от M+1 до N) весом будет их порядковый номер минус M, но с небольшим дробным смещением (например, 0.1 или 0.5), чтобы они встали сразу после соответствующих записей из первой группы.
Посмотрим на пример реализации в тексте запроса. Сначала нам необходимо пронумеровать записи во временной таблице:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
АВТОНОМЕРЗАПИСИ() КАК НомерЗаписи
ПОМЕСТИТЬ ВТ_ПронумерованныеДанные
ИЗ
Справочник.Номенклатура КАК Номенклатура
УПОРЯДОЧИТЬ ПО
Номенклатура.Наименование
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Ссылка,
ВЫБОР
КОГДА ВТ.НомерЗаписи <= &Середина
ТОГДА ВТ.НомерЗаписи
ИНАЧЕ ВТ.НомерЗаписи - &Середина + 0.1
КОНЕЦ КАК ВесСортировки
ИЗ
ВТ_ПронумерованныеДанные КАК ВТ
УПОРЯДОЧИТЬ ПО
ВесСортировки
В данном примере, если у нас 100 записей и &Середина равна 50, то запись №1 получит вес 1, а запись №51 получит вес 51 - 50 + 0.1 = 1.1. При сортировке по возрастанию запись с весом 1.1 встанет сразу после записи с весом 1. Таким образом, мы получим нужный порядок: 1, 51, 2, 52...
Рассмотрим альтернативный подход, предложенный участниками сообщества. Он заключается в разделении данных на две временные таблицы и их последующем соединении по вычисляемому ключу. Выясним причину популярности этого метода: он нагляден и позволяет четко разделить потоки данных.
Сначала сформируем общую таблицу с номерами строк. Важно помнить, что функция АвтономерЗаписи() работает стабильно только внутри временных таблиц при наличии инструкции УПОРЯДОЧИТЬ ПО. Далее разделим выборку на две части и соединим их:
// Предположим, общее количество записей 100, &Середина = 50
ВЫБРАТЬ
Т1.Ссылка КАК Ссылка1,
Т2.Ссылка КАК Ссылка2
ИЗ
ВТ_ПронумерованныеДанные КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПронумерованныеДанные КАК Т2
ПО (Т1.НомерЗаписи + &Середина = Т2.НомерЗаписи)
ГДЕ
Т1.НомерЗаписи <= &Середина
Этот метод удобен, если вам нужно вывести данные в две колонки одного отчета. Однако, если целью является именно чередование строк в одной колонке, этот результат придется дополнительно обрабатывать или использовать ОБЪЕДИНИТЬ ВСЕ с последующей сортировкой, что делает запрос более громоздким по сравнению с первым методом.
Разберем еще один изящный способ формирования индекса. Мы можем математически вычислить итоговую позицию строки так, чтобы записи из первой половины заняли нечетные позиции (1, 3, 5...), а записи из второй половины — четные (2, 4, 6...).
Формула для первой половины (i <= M): Индекс = i * 2 - 1.
Формула для второй половины (i > M): Индекс = (i - M) * 2.
Реализуем это в запросе:
ВЫБРАТЬ
ВТ.Ссылка,
ВЫБОР
КОГДА ВТ.НомерЗаписи <= &Середина
ТОГДА ВТ.НомерЗаписи * 2 - 1
ИНАЧЕ (ВТ.НомерЗаписи - &Середина) * 2
КОНЕЦ КАК ПорядковыйНомер
ИЗ
ВТ_ПронумерованныеДанные КАК ВТ
УПОРЯДОЧИТЬ ПО
ПорядковыйНомер
Этот метод гарантирует получение строгой последовательности целых чисел, что может быть полезно для дальнейших манипуляций с данными или вывода в специфические форматы файлов.
Если задача стоит в рамках разработки отчета на СКД, у нас есть несколько путей. Мы можем либо использовать вышеописанные методы в тексте запроса (что предпочтительнее для производительности), либо применить Вычисляемые поля.
В СКД можно добавить вычисляемое поле, которое будет обращаться к функции общего модуля для расчета индекса, однако стоит учитывать, что СКД не всегда может эффективно отсортировать данные по такому полю «на лету» без предварительной подготовки в запросе. Проанализируем ситуацию: в компоновщике данных лучше всего подготовить поле ВесСортировки на уровне набора данных (запроса), а в настройках на вкладке «Сортировка» просто указать это поле.
Бывают ситуации, когда данные уже получены в виде ТаблицыЗначений или ВыборкиИзЗапроса, и менять запрос нельзя. В этом случае мы можем применить циклический обход. Рассмотрим пример кода на встроенном языке:
КоличествоСтрок = ТаблицаДанных.Количество();
Середина = Цел(КоличествоСтрок / 2);
// Если количество строк нечетное, прибавляем 1 к середине для корректного деления
Если КоличествоСтрок % 2 <> 0 Тогда
Середина = Середина + 1;
КонецЕсли;
Для Счетчик = 0 По Середина - 1 Цикл
// Обрабатываем запись из первой половины
Запись1 = ТаблицаДанных[Счетчик];
ОбработатьСтроку(Запись1);
// Проверяем, есть ли парная запись во второй половине
Если Счетчик + Середина < КоличествоСтрок Тогда
Запись2 = ТаблицаДанных[Счетчик + Середина];
ОбработатьСтроку(Запись2);
КонецЕсли;
КонецЦикла;
Этот алгоритм корректно отрабатывает как четное, так и нечетное общее количество строк. Мы берем строку из начала, затем строку со смещением на длину «половины», имитируя требуемый порядок вывода.
Проанализируем ключевые моменты, которые могут повлиять на корректность результата:
N = 101. В этом случае первая «половина» должна быть 51 запись, а вторая — 50. Округление при расчете переменной &Середина должно идти в большую сторону.АвтономерЗаписи() без явного УПОРЯДОЧИТЬ ПО. Без этого порядок строк в таблице может измениться при следующем запуске запроса или обновлении статистики СУБД, что полностью нарушит логику чередования.Таким образом, мы рассмотрели различные способы решения задачи: от чисто математических методов в запросах до программной обработки коллекций. Выбор конкретного метода зависит от того, где именно должны быть выведены данные — в печатной форме, в отчете СКД или в динамическом списке.