При разработке отчетов на схеме компоновки данных (СКД) периодически возникает задача «размножить» или «развернуть» одну строку исходного документа на несколько строк в финальном отчете. Классический пример: в документе указан товар и его количество (например, «Товар 1» в количестве 3 штук). Нам же требуется вывести в отчет три отдельные строки для этого товара, причем для каждой строки необходимо сформировать уникальный серийный номер или параметр вида НомерДокумента_НомерСтроки_ПорядковыйНомер.
Многие разработчики сразу начинают писать программный код, выгружать результат запроса в таблицу значений, обрабатывать ее в цикле на встроенном языке 1С и затем передавать готовую таблицу обратно в СКД в качестве внешнего набора данных. Однако такое решение лишает нас преимуществ автоматических настроек СКД, усложняет поддержку отчета и снижает его производительность.
В этой статье мы подробно разберем, как решить данную задачу исключительно средствами СКД и языка запросов, не прибегая к программированию модулей.
Самый производительный, чистый и надежный способ размножить строки прямо в запросе СКД — это использование генератора числовой последовательности на основе так называемого декартова произведения (оператора CROSS JOIN).
Идея заключается в том, чтобы создать виртуальную таблицу, содержащую непрерывный ряд чисел от 1 до максимально возможного количества товара в одной строке (например, до 1000). Затем мы соединим нашу таблицу товаров с этой числовой таблицей по условию «меньше или равно количеству».
Проанализируем этот процесс по шагам.
Шаг 1. Создание базовой таблицы цифр. Для начала сформируем временную таблицу, содержащую цифры от 0 до 9. Мы сделаем это в первом пакете запроса с помощью объединения предопределенных строк:
ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ ВТ_Цифры
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9
;
Шаг 2. Генерация матрицы чисел (числовой последовательности). Теперь, соединяя таблицу ВТ_Цифры саму с собой без жестких условий связи (перекрестное соединение), мы можем легко получить последовательность чисел любого нужного объема. Например, соединив таблицу трижды, мы получим последовательность от 0 до 999. Посмотрим на этот запрос:
ВЫБРАТЬ
t1.Цифра + t2.Цифра * 10 + t3.Цифра * 100 КАК Число
ПОМЕСТИТЬ ВТ_МатрицаЧисел
ИЗ
ВТ_Цифры КАК t1
КРОСС СОЕДИНЕНИЕ ВТ_Цифры КАК t2
КРОСС СОЕДИНЕНИЕ ВТ_Цифры КАК t3
;
Шаг 3. Соединение исходных данных с матрицей чисел. На этом этапе мы выбираем данные из нашего документа и связываем их с полученной матрицей чисел. Условием связи будет являться попадание числа из матрицы в диапазон от 1 до количества товара в строке документа:
ВЫБРАТЬ
ТоварыДокумента.Ссылка.Номер КАК НомерДокумента,
ТоварыДокумента.НомерСтроки КАК НомерСтрокиДокумента,
ТоварыДокумента.Номенклатура КАК Номенклатура,
ТоварыДокумента.Количество КАК ИсходноеКоличество,
ВТ_МатрицаЧисел.Число КАК ПорядковыйНомерСтроки
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТоварыДокумента
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МатрицаЧисел КАК ВТ_МатрицаЧисел
ПО ВТ_МатрицаЧисел.Число >= 1
И ВТ_МатрицаЧисел.Число <= ТоварыДокумента.Количество
Выясним, что происходит в СУБД при таком соединении. Если в документе содержится строка с «Товаром 1» в количестве 3 штук, СУБД найдет в таблице ВТ_МатрицаЧисел строки с числами 1, 2 и 3 (так как они удовлетворяют условию больше или равно 1 и меньше или равно 3). В итоге в результат запроса попадет ровно 3 строки для этой номенклатуры, и каждая строка получит свой уникальный ПорядковыйНомерСтроки.
Согласно условию задачи, нам необходимо получить выходной параметр вида 821_1_1, где первая часть — номер документа, вторая — номер строки в документе, а третья — уникальный порядковый номер внутри количества товара.
Попробуем сделать это непосредственно в запросе. Однако в языке запросов 1С отсутствует простая функция приведения типов из числа в строку (функция ВЫРАЗИТЬ умеет работать только со строками ограниченной длины и не переводит числовой тип в строковый). Попытки обойти это ограничение в запросе приводят к созданию громоздких и непроизводительных конструкций.
Решим эту проблему элегантно с помощью механизма Вычисляемых полей в СКД. Платформа 1С позволяет использовать полноценный язык выражений СКД, который без проблем преобразует типы данных:
ПараметрСтрока(НомерДокумента) + "_" + Строка(НомерСтрокиДокумента) + "_" + Строка(ПорядковыйНомерСтроки)ПараметрБлагодаря этому подходу платформа автоматически преобразует все числа в строки, уберет неразрывные пробелы группировки разрядов чисел и сформирует красивую строковую последовательность на этапе вывода отчета.
Рассмотрим ситуацию, когда требования усложняются: разворачивать строки нужно не для всех товаров, а только для определенной категории или типа номенклатуры (например, для штучного или подакцизного товара), в то время как услуги или весовой товар должны остаться одной строкой.
Для реализации этого требования нам достаточно изменить условие связи с нашей матрицей чисел в запросе СКД с помощью конструкции ВЫБОР:
ВЫБРАТЬ
ТоварыДокумента.Ссылка.Номер КАК НомерДокумента,
ТоварыДокумента.НомерСтроки КАК НомерСтрокиДокумента,
ТоварыДокумента.Номенклатура КАК Номенклатура,
ТоварыДокумента.Количество КАК ИсходноеКоличество,
ВТ_МатрицаЧисел.Число КАК ПорядковыйНомерСтроки
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТоварыДокумента
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МатрицаЧисел КАК ВТ_МатрицаЧисел
ПО ВТ_МатрицаЧисел.Число >= 1
И ВТ_МатрицаЧисел.Число <= ВЫБОР
КОГДА ТоварыДокумента.Номенклатура.ВидНоменклатуры.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Услуга)
ТОГДА 1
ИНАЧЕ ТоварыДокумента.Количество
КОНЕЦ
Проанализируем работу этого условия: если номенклатура является «Услугой», то условие связи ограничит выборку из матрицы чисел только цифрой 1 (строка не размножится). Если же это обычный товар, то связь произойдет по полному количеству, и строка развернется на нужное количество позиций.
Иногда разработчики пытаются решить эту задачу путем создания двух наборов данных (Набор данных - запрос) и их связывания на вкладке «Связи наборов данных» по условию «меньше или равно». Нам важно понимать ограничения платформы 1С в данном контексте:
=). Платформа напрямую не поддерживает операторы сравнения типа >= или <= во вкладке связей.ГДЕ Число <= &ПараметрКоличество). Однако это заставит СКД выполнять запрос второго набора в цикле для каждого товара из первого набора. Если в документе много строк, это приведет к катастрофическому падению производительности отчета.Именно поэтому использование единого запроса с временной таблицей декартова произведения (Решение 1) является наиболее быстрым, масштабируемым и профессиональным методом размножения строк в компоновке данных.