Как продублировать строки в запросе или СКД по значению количества

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

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

Такая потребность часто возникает в следующих типовых сценариях:

  1. Печать ценников, этикеток и штрихкодов: когда на каждую единицу номенклатуры из документа или остатка требуется вывести отдельную наклейку или бирку.
  2. Поштучный учет и маркировка: разбиение партий на единичные экземпляры для последующего ввода индивидуальных серийных номеров, кодов маркировки или гарантийных талонов.
  3. Экспорт данных: выгрузка во внешние логистические системы, требующие построчной детализации каждого физического места вместо агрегированного количества.

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

Способ 1. Соединение со статической таблицей чисел в запросе

Рассмотрим классический прием: создание временной таблицы с последовательностью натуральных чисел и последующее неэквивалентное соединение с исходными данными по условию ВТЧисла.Число <= ИсходныеДанные.Количество. Если заранее известно, что количество объектов не превышает небольшого значения (например, от 1 до 10), можно собрать вспомогательную таблицу через конструкцию ОБЪЕДИНИТЬ ВСЕ.

Посмотрим на пример текста запроса:


ВЫБРАТЬ 1 КАК Число ПОМЕСТИТЬ ВТНомера
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 10
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ИсходныеДанные.Номенклатура КАК Номенклатура,
    1 КАК Количество,
    ИсходныеДанные.Количество КАК ИсходноеКоличество,
    ВТНомера.Число КАК НомерПоПорядку
ИЗ
    ВТ_Данные КАК ИсходныеДанные
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТНомера КАК ВТНомера
        ПО ВТНомера.Число <= ИсходныеДанные.Количество

В результате такого соединения строка, где количество равно 1, соединится только с числом 1 (даст 1 строку). Строка с количеством 3 соединится с числами 1, 2 и 3, породив ровно 3 строки с сохранением порядкового номера экземпляра.

Способ 2. Генерация числовой последовательности до 1000 строк перемножением таблиц

Если максимальное количество заранее точно не ограничено десятью единицами, но может достигать сотен, вручную писать сотни строк с ОБЪЕДИНИТЬ ВСЕ нерационально. Проанализируем, как сгенерировать последовательность чисел от 1 до 1000 с помощью декартова произведения цифр разрядов (единицы, десятки, сотни):


ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ ВТЦифры
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Единицы.Цифра + Десятки.Цифра * 10 + Сотни.Цифра * 100 + 1 КАК Номер
ПОМЕСТИТЬ ВТЧисла
ИЗ
    ВТЦифры КАК Единицы
        СОЕДИНЕНИЕ ВТЦифры КАК Десятки ПО (ИСТИНА)
        СОЕДИНЕНИЕ ВТЦифры КАК Сотни ПО (ИСТИНА)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    Товары.Количество КАК ВсегоВДокументе,
    1 КАК КоличествоВСтроке,
    ВТЧисла.Номер КАК НомерЭкземпляра
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТЧисла КАК ВТЧисла
        ПО ВТЧисла.Номер <= Товары.Количество
ГДЕ
    Товары.Ссылка = &Ссылка

Способ 3. Передача таблицы чисел через параметр запроса

Чтобы не перегружать текст запроса вспомогательными выборками цифр, сформируем вспомогательную таблицу чисел программно на встроенном языке 1С и передадим ее в параметр запроса:


// Подготовим таблицу чисел нужного размера
ТаблицаЧисел = Новый ТаблицаЗначений;
ТаблицаЧисел.Колонки.Добавить("Число", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));

МаксимальноеКоличество = 500; // или вычисляем максимум из выборки
Для Счетчик = 1 По МаксимальноеКоличество Цикл
    НоваяСтрока = ТаблицаЧисел.Добавить();
    НоваяСтрока.Число = Счетчик;
КонецЦикла;

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Т.Число КАК Число
    |ПОМЕСТИТЬ ВТЧисла
    |ИЗ
    |   &ТаблицаЧисел КАК Т
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   Остатки.Номенклатура КАК Номенклатура,
    |   1 КАК Количество,
    |   ВТЧисла.Число КАК НомерКопии
    |ИЗ
    |   РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
    |       ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТЧисла КАК ВТЧисла
    |       ПО ВТЧисла.Число <= Остатки.ВНаличииОстаток";

Запрос.УстановитьПараметр("ТаблицаЧисел", ТаблицаЧисел);
Результат = Запрос.Выполнить();

Способ 4. Размножение строк во внешнем наборе данных СКД

Если отчет строится с использованием СКД, альтернативным вариантом является использование набора данных «Объект». Мы можем получить агрегированные данные обычным быстрым запросом, размножить строки циклом в коде модуля отчета или формы и передать готовую плоскую таблицу в СКД через ВнешниеНаборыДанных.

Разберем пример размножения строк в коде 1С:


ТаблицаИсходная = Запрос.Выполнить().Выгрузить();
ТаблицаРезультат = ТаблицаИсходная.СкопироватьКолонки();
ТаблицаРезультат.Колонки.Добавить("НомерЭкземпляра", Новый ОписаниеТипов("Число"));

Для Каждого СтрДанных Из ТаблицаИсходная Цикл
    КоличествоИтераций = Цел(СтрДанных.Количество);
    Для Счетчик = 1 По КоличествоИтераций Цикл
        СтрНовая = ТаблицаРезультат.Добавить();
        ЗаполнитьЗначенияСвойств(СтрНовая, СтрДанных);
        СтрНовая.Количество = 1;
        СтрНовая.НомерЭкземпляра = Счетчик;
    КонецЦикла;
КонецЦикла;

ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ТаблицаДанных", ТаблицаРезультат);

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных);

Важные ограничения и особенности работы

При использовании описанных способов необходимо учитывать несколько критически важных нюансов платформы:

  1. Задвоение сумм и ресурсов в СКД: если в исходной строке была сумма, например, 1000 рублей при количестве 5 шт., то при обычном размножении строк мы получим 5 строк по 1000 рублей. Если поле назначено ресурсом со стандартным выражением Сумма(Сумма), общий итог составит 5000 рублей вместо 1000. В запросе или выражении ресурса СКД обязательно пересчитывайте цену за единицу: Сумма / Количество.
  2. Дробные значения количества: неэквивалентное соединение с целыми числами по условию <= некорректно обрабатывает дробные остатки (например, 2.5). Дробная часть отсечется, либо возникнет логическая ошибка. При наличии дробных величин необходимо округлять или приводить поле к целому: ВЫРАЗИТЬ(Количество КАК ЧИСЛО(15, 0)).
  3. Нулевые и отрицательные остатки: при ВНУТРЕННЕМ СОЕДИНЕНИИ строки с нулевым или отрицательным количеством полностью исключаются из результирующей выборки. Если такие позиции должны отображаться в отчете, применяйте ЛЕВОЕ СОЕДИНЕНИЕ и обрабатывайте пустые значения через ЕСТЬNULL.
  4. Производительность СУБД: соединения по условию <= транслируются СУБД во вложенные циклы (Nested Loops). На десятках тысяч строк такой запрос может выполняться заметно медленнее, поэтому для огромных массивов данных предпочтительнее программная постобработка строк.
← На главную