При разработке отчетов и обработок в 1С иногда возникает нестандартная задача: требуется выгрузить табличную часть документа так, чтобы каждая строка с количеством N превратилась в N отдельных строк с количеством 1 и пересчитанной ценой (или суммой на одну единицу). Рассмотрим подробнее, как реализовать подобную логику исключительно средствами языка запросов 1С, а также проанализируем нюансы данного подхода и альтернативные способы решения.
Представим, что в табличной части документа присутствует строка:
Номенклатура: Сахар-песок, Количество: 5, Сумма: 500 руб.
Нам необходимо получить в результате запроса 5 отдельных строк вида:
Номенклатура: Сахар-песок, Количество: 1, Цена: 100 руб.
В языке запросов 1С отсутствует встроенный цикл или функция размножения записей. Чтобы решить эту задачу одним запросом, применяется так называемый порождающий запрос чисел (генератор последовательности целых чисел). Построим временную таблицу, содержащую численный ряд от 1 до максимального количества, а затем соединим ее с табличной частью по условию неравенства ТабличнаяЧасть.Количество >= Числа.Число.
Разберем по шагам готовый запрос, выполняющий генерацию порядка чисел и умножение строк исходного документа.
Шаг 1. Формирование таблицы цифр и получение последовательности чисел
Сначала сформируем первичную табличную часть из 10 цифр (от 0 до 9), а затем с помощью полного соединения сформируем числовую последовательность разрядов:
ВЫБРАТЬ
0 КАК Ноль,
1 КАК Единица,
2 КАК Двойка,
3 КАК Тройка,
4 КАК Четверка,
5 КАК Пятерка,
6 КАК Шестерка,
7 КАК Семерка,
8 КАК Восьмерка,
9 КАК Девятка
ПОМЕСТИТЬ ЦифрыВСтолбцах
;
ВЫБРАТЬ
ЕСТЬNULL(Цифра0.Ноль, ЕСТЬNULL(Цифра1.Единица, ЕСТЬNULL(Цифра2.Двойка, ЕСТЬNULL(Цифра3.Тройка, ЕСТЬNULL(Цифра4.Четверка, ЕСТЬNULL(Цифра5.Пятерка, ЕСТЬNULL(Цифра6.Шестерка, ЕСТЬNULL(Цифра7.Семерка, ЕСТЬNULL(Цифра8.Восьмерка, ЕСТЬNULL(Цифра9.Девятка, 0)))))))))) КАК Цифра
ПОМЕСТИТЬ Цифры
ИЗ
ЦифрыВСтолбцах КАК Цифра0
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра1 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра2 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра3 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра4 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра5 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра6 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра7 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра8 ПО (ЛОЖЬ)
ПОЛНОЕ СОЕДИНЕНИЕ ЦифрыВСтолбцах КАК Цифра9 ПО (ЛОЖЬ)
;
Шаг 2. Генерация числового ряда до нужного верхнего предела
Путем комбинирования разрядов создаем временную таблицу Числа, содержащую последовательный ряд натуральных чисел от 1 до значения параметра &ВерхнийПредел:
ВЫБРАТЬ
1 + Единицы.Цифра + 10 * ЕСТЬNULL(Десятки.Цифра, 0) + 100 * ЕСТЬNULL(Сотни.Цифра, 0) + 1000 * ЕСТЬNULL(Тысячи.Цифра, 0) КАК Число
ПОМЕСТИТЬ Числа
ИЗ
Цифры КАК Единицы
ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Десятки
ПО (&ВерхнийПредел >= 10)
ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Сотни
ПО (&ВерхнийПредел >= 100)
ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Тысячи
ПО (&ВерхнийПредел >= 1000)
ГДЕ
1 + Единицы.Цифра + 10 * ЕСТЬNULL(Десятки.Цифра, 0) + 100 * ЕСТЬNULL(Сотни.Цифра, 0) + 1000 * ЕСТЬNULL(Тысячи.Цифра, 0) <= &ВерхнийПредел
;
Шаг 3. Выборка данных табличной части и размножение строк
Проанализируем заключительный этап: помещаем данные из документа во временную таблицу ТабличнаяЧасть и выполняем внутреннее соединение с таблицей Числа по условию неравенства:
ВЫБРАТЬ
ПриходнаяНакладнаяСостав.Ссылка КАК Документ,
ПриходнаяНакладнаяСостав.Номенклатура КАК Номенклатура,
ПриходнаяНакладнаяСостав.Количество КАК Количество,
ПриходнаяНакладнаяСостав.Сумма / ПриходнаяНакладнаяСостав.Количество КАК Цена
ПОМЕСТИТЬ ТабличнаяЧасть
ИЗ
Документ.ПриходнаяНакладная.Состав КАК ПриходнаяНакладнаяСостав
ГДЕ
НЕ ПриходнаяНакладнаяСостав.Количество = 0
И НЕ ПриходнаяНакладнаяСостав.Сумма = 0
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТабличнаяЧасть.Документ КАК Документ,
ТабличнаяЧасть.Номенклатура КАК Номенклатура,
ТабличнаяЧасть.Цена КАК Цена,
1 КАК Количество
ИЗ
ТабличнаяЧасть КАК ТабличнаяЧасть
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Числа КАК Числа
ПО ТабличнаяЧасть.Количество >= Числа.Число
;
В результате внутреннее соединение с таблицей чисел продублирует каждую строку столько раз, каково значение поля Количество в исходной табличной части.
При использовании этого приема важно учитывать технические особенности платформы 1С:
Количество >= Число рассчитано на целые числа. Если позиция содержит дробное количество (например, 1.5 кг), то при сравнении с целыми числами сформируется лишь одна строка, а дробная часть количества затеряется.Сумма / Количество при некратных числах может давать бесконечные дроби. При последующем суммировании итоговая сумма может отличаться от исходной суммы документа на несколько копеек. В запросе 1С невозможно автоматически скорректировать копейку на последнюю строку без алгоритмического кода.>=) с большими числами создает серьезную нагрузку на сервер баз данных и временные таблицы (TempDB), так как такое условие не может использовать стандартные индексы.Рассмотрим более простые и устойчивые варианты решения задачи в коде 1С.
1. Передача таблицы чисел через параметр запроса
Вместо сложного генератора цифр внутри текста запроса можно сгенерировать простую таблицу чисел встроенным языком 1С и передать ее как параметр или поместить в менеджер временных таблиц:
ТаблицаЧисел = Новый ТаблицаЗначений;
ТаблицаЧисел.Колонки.Добавить("Число", Новый ОписаниеТипов("Число"));
Для Счетчик = 1 По МаксКоличество Цикл
НоваяСтрока = ТаблицаЧисел.Добавить();
НоваяСтрока.Число = Счетчик;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Число ПОМЕСТИТЬ Числа ИЗ &ТаблицаЧисел КАК ТаблицаЧисел;";
Запрос.УстановитьПараметр("ТаблицаЧисел", ТаблицаЧисел);
2. Процедурный обход во внешнем цикле
Самым надежным и быстродействующим методом остается обычная выборка строк запросом и их последующее размножение во внешнем цикле модуля 1С. Это позволяет гибко распределять округления копеек и корректно обрабатывать нецелые количества.