Как в запросе 1С сгруппировать результат по определенному количеству строк

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

В процессе разработки на платформе 1С:Предприятие часто возникают задачи, когда необходимо обработать данные не целиком, а порциями. Например, при выгрузке данных во внешние системы, формировании многостраничных отчетов или выполнении тяжелых операций в фоновых заданиях. Одной из самых интересных подзадач здесь является разделение (группировка) результата запроса на блоки по заданному числу строк (например, по 100, 500 или 1000 записей).

В этой статье мы подробно разберем, как реализовать такую группировку непосредственно в тексте запроса, а также рассмотрим альтернативные способы с использованием Системы компоновки данных (СКД) и программной обработки временных таблиц.

Метод 1: Математическое вычисление индекса группы в запросе

Если в вашем источнике данных уже есть порядковый номер (например, поле НомерСтроки в табличной части документа), мы можем применить математическую формулу для вычисления номера группы. Основная сложность заключается в том, что в языке запросов 1С нет функции деления нацело или взятия остатка, но есть функция ВЫРАЗИТЬ, которая выполняет арифметическое округление.

Рассмотрим ситуацию, когда нам нужно сгруппировать строки по 100 штук. Разберем логику вычисления:


ВЫБРАТЬ
    ВЫРАЗИТЬ((ТЧ.НомерСтроки + 49) / 100 КАК ЧИСЛО(10, 0)) КАК НомерГруппы,
    ТЧ.Номенклатура,
    ТЧ.Количество
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
ГДЕ
    ТЧ.Ссылка = &Ссылка

Почему это работает? Проанализируем математику округления в 1С. Функция ВЫРАЗИТЬ(... КАК ЧИСЛО(10, 0)) работает по принципу арифметического округления: значения меньше 0.5 округляются вниз, а 0.5 и выше — вверх.

  1. Для 1-й строки: (1 + 49) / 100 = 0.5. Округляется до 1.
  2. Для 50-й строки: (50 + 49) / 100 = 0.99. Округляется до 1.
  3. Для 100-й строки: (100 + 49) / 100 = 1.49. Округляется до 1.
  4. Для 101-й строки: (101 + 49) / 100 = 1.5. Округляется до 2.

Таким образом, мы получаем универсальный счетчик групп. Если вам нужно другое количество строк (N), формула примет вид: (НомерСтроки + (N/2 - 1)) / N. Важно помнить, что этот метод идеально подходит для табличных частей, где поле НомерСтроки определено изначально.

Метод 2: Формирование порядкового номера для произвольных данных

Что делать, если мы работаем не с табличной частью, а со справочником или регистром, где нет готового поля НомерСтроки? В этом случае нам необходимо сначала пронумеровать записи. Рассмотрим способ с использованием "самосоединения" таблицы.

Разберем по шагам создание нумерации в запросе:


ВЫБРАТЬ
    Т1.Ссылка КАК Ссылка,
    КОЛИЧЕСТВО(Т2.Ссылка) КАК ПорядковыйНомер
ПОМЕСТИТЬ ВТ_Нумерация
ИЗ
    Справочник.Номенклатура КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Т2
        ПО Т1.Ссылка >= Т2.Ссылка
ГРУППИРОВАТЬ ПО
    Т1.Ссылка;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВЫРАЗИТЬ((ВТ_Нумерация.ПорядковыйНомер + 49) / 100 КАК ЧИСЛО(10, 0)) КАК НомерГруппы,
    ВТ_Нумерация.Ссылка
ИЗ
    ВТ_Нумерация КАК ВТ_Нумерация

Проанализируем этот подход: мы соединяем таблицу саму с собой по условию "больше или равно". Для каждой записи количество подходящих записей из второй таблицы будет соответствовать её порядковому номеру. Внимание: данный метод является ресурсозатратным (сложность O(n²)) и не рекомендуется для таблиц объемом более нескольких тысяч записей.

Метод 3: Использование Системы компоновки данных (СКД)

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

В СКД доступны системные поля, такие как НомерПоПорядку. Мы можем создать Вычисляемое поле с выражением, использующим функции общего модуля. Например, создадим функцию в общем модуле:


Функция ПолучитьНомерГруппы(ПорядковыйНомер, Порция) Экспорт
    Возврат Цел((ПорядковыйНомер - 1) / Порция) + 1;
КонецФункции

Затем в настройках СКД на закладке "Вычисляемые поля" добавим поле НомерГруппы с выражением: ИмяОбщегоМодуля.ПолучитьНомерГруппы(НомерПоПорядку, 100). Это позволит гибко управлять группировкой прямо в настройках отчета, не меняя основной запрос.

Метод 4: Программная подготовка данных во временной таблице

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

  1. Выполняем запрос для получения данных во временную таблицу.
  2. Выгружаем временную таблицу в ТаблицаЗначений.
  3. В цикле по таблице значений заполняем колонку НомерГруппы, используя целочисленное деление.
  4. Передаем модифицированную таблицу значений обратно в менеджер временных таблиц для дальнейшего использования в запросах.

// Пример заполнения в коде
Индекс = 0;
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
    СтрокаТЗ.НомерГруппы = Цел(Индекс / 100) + 1;
    Индекс = Индекс + 1;
КонецЦикла;

Этот метод часто оказывается эффективнее "чистого" SQL-запроса на больших объемах, так как обход коллекции в памяти 1С выполняется очень быстро, а сервер СУБД избавляется от тяжелых вычислений при соединении таблиц.

Важные рекомендации по сортировке

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

Используйте уникальные идентификаторы (поле Ссылка) или системное поле НомерСтроки для обеспечения детерминированного результата при каждой итерации группировки.

← На главную