Как вывести данные в запросе или СКД в определенном порядке (чередование записей 1, 51, 2, 52...)

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработчика 1С иногда возникают задачи по нестандартному упорядочиванию данных. Например, когда необходимо вывести записи «в два столбца» или особым образом чередовать строки: сначала первую запись из первой половины списка, затем первую запись из второй половины, и так далее (1, 51, 2, 52...). Рассмотрим подробно, как реализовать такой алгоритм с помощью запросов, системы компоновки данных (СКД) и программного кода.

Метод 1. Использование математического веса сортировки в запросе

Проанализируем наиболее эффективный способ, который не требует сложных соединений таблиц и основан на вычислении «веса» для каждой строки. Идея заключается в том, чтобы присвоить каждой записи числовой индекс, по которому СУБД выполнит итоговую сортировку. Предположим, у нас есть общее количество записей 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...

Метод 2. Использование функции АвтономерЗаписи и соединения таблиц

Рассмотрим альтернативный подход, предложенный участниками сообщества. Он заключается в разделении данных на две временные таблицы и их последующем соединении по вычисляемому ключу. Выясним причину популярности этого метода: он нагляден и позволяет четко разделить потоки данных.

Сначала сформируем общую таблицу с номерами строк. Важно помнить, что функция АвтономерЗаписи() работает стабильно только внутри временных таблиц при наличии инструкции УПОРЯДОЧИТЬ ПО. Далее разделим выборку на две части и соединим их:


// Предположим, общее количество записей 100, &Середина = 50
ВЫБРАТЬ
    Т1.Ссылка КАК Ссылка1,
    Т2.Ссылка КАК Ссылка2
ИЗ
    ВТ_ПронумерованныеДанные КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПронумерованныеДанные КАК Т2
        ПО (Т1.НомерЗаписи + &Середина = Т2.НомерЗаписи)
ГДЕ
    Т1.НомерЗаписи <= &Середина

Этот метод удобен, если вам нужно вывести данные в две колонки одного отчета. Однако, если целью является именно чередование строк в одной колонке, этот результат придется дополнительно обрабатывать или использовать ОБЪЕДИНИТЬ ВСЕ с последующей сортировкой, что делает запрос более громоздким по сравнению с первым методом.

Метод 3. Аналитический подход «Четное-Нечетное»

Разберем еще один изящный способ формирования индекса. Мы можем математически вычислить итоговую позицию строки так, чтобы записи из первой половины заняли нечетные позиции (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);
    КонецЕсли;
КонецЦикла;

Этот алгоритм корректно отрабатывает как четное, так и нечетное общее количество строк. Мы берем строку из начала, затем строку со смещением на длину «половины», имитируя требуемый порядок вывода.

Важные нюансы и производительность

Проанализируем ключевые моменты, которые могут повлиять на корректность результата:

  1. Нечетное количество записей: Всегда проверяйте, как алгоритм ведет себя при N = 101. В этом случае первая «половина» должна быть 51 запись, а вторая — 50. Округление при расчете переменной &Середина должно идти в большую сторону.
  2. Производительность: Методы с расчетом веса внутри одного запроса (без Self-Join) работают быстрее на больших объемах данных. Соединение таблицы самой с собой (Self-Join) создает дополнительную нагрузку на СУБД, так как требует сопоставления каждой строки с каждой перед применением фильтра.
  3. Стабильность нумерации: Никогда не используйте АвтономерЗаписи() без явного УПОРЯДОЧИТЬ ПО. Без этого порядок строк в таблице может измениться при следующем запуске запроса или обновлении статистики СУБД, что полностью нарушит логику чередования.

Таким образом, мы рассмотрели различные способы решения задачи: от чисто математических методов в запросах до программной обработки коллекций. Выбор конкретного метода зависит от того, где именно должны быть выведены данные — в печатной форме, в отчете СКД или в динамическом списке.

← На главную