Как в запросе 1С размножить строки по значению в колонке «Количество»?

Программист Торговля и дистрибуция
← На главную

Часто перед разработчиками 1С встает задача: как из одной строки табличной части документа, где указана номенклатура и ее количество, получить несколько строк, где каждая соответствует одной единице товара. Например, строку «Помидор, 7 шт.» нужно преобразовать в семь отдельных строк «Помидор, 1 шт.». Это может быть необходимо для печати этикеток, формирования отчетов по серийным номерам или для других специфических задач. Разберем, как решить эту задачу исключительно средствами языка запросов 1С.

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

Шаг 1. Создание временной таблицы с последовательностью чисел

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

Давайте создадим ряд чисел от 1 до 1000. Сначала сгенерируем таблицу с цифрами от 0 до 9.


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

Теперь, используя эту базовую таблицу РядЧисел, сгенерируем числа от 1 до 1000. Мы сделаем это, соединив таблицу саму с собой три раза, что даст нам 10 * 10 * 10 = 1000 комбинаций. Каждая комбинация будет соответствовать одному числу.


ВЫБРАТЬ
    РядЧисел2.Числа * 100 + РядЧисел1.Числа * 10 + РядЧисел.Числа + 1 КАК Число
ПОМЕСТИТЬ ВТ_РядЧисел
ИЗ
    РядЧисел КАК РядЧисел,
    РядЧисел КАК РядЧисел1,
    РядЧисел КАК РядЧисел2

Важный момент: Максимальное число в этой таблице (в нашем случае 999+1=1000) должно быть больше или равно максимальному возможному значению в поле Количество в вашей исходной таблице. Если у вас количество может быть больше 1000, вам нужно добавить еще одно соединение с таблицей РядЧисел.

Шаг 2. Соединение исходных данных с таблицей чисел

Теперь у нас есть все необходимое для решения основной задачи. Мы должны соединить нашу исходную таблицу (например, табличную часть документа) с созданной временной таблицей ВТ_РядЧисел.

Ключевой элемент здесь — условие соединения. Нам нужно, чтобы для каждой строки из исходной таблицы было создано столько же дубликатов, сколько указано в поле Количество.

Проанализируем пример запроса из обсуждения на форуме, в котором была допущена распространенная ошибка.

Неправильный вариант:


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

Давайте разберем, почему условие ТОРГ_ПеремещениеТовары.Количество <= ВТ_РядЧисел.Число неверно. Если у нас есть строка с Количество = 7, это условие будет истинным для всех чисел из ВТ_РядЧисел, которые больше или равны 7 (т.е. 7, 8, 9, ..., 1000). В результате мы получим не 7 строк, а 994 строки, что является ошибкой.

Правильный вариант:

Чтобы получить правильный результат, условие соединения нужно инвертировать. Для каждой строки исходной таблицы нам нужно взять из таблицы чисел все значения от 1 до значения в поле Количество.

Корректное условие будет выглядеть так: ВТ_РядЧисел.Число <= ТОРГ_ПеремещениеТовары.Количество.


ВЫБРАТЬ
    ТОРГ_ПеремещениеТовары.Номенклатура,
    1 КАК Количество,
    ВТ_РядЧисел.Число КАК ПорядковыйНомер // Можно вывести для наглядности
ИЗ
    Документ.ТОРГ_Перемещение.Товары КАК ТОРГ_ПеремещениеТовары
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_РядЧисел КАК ВТ_РядЧисел
        ПО ВТ_РядЧисел.Число <= ТОРГ_ПеремещениеТовары.Количество // Правильное условие
ГДЕ
    ТОРГ_ПеремещениеТовары.Ссылка = &Ссылка
    И ТОРГ_ПеремещениеТовары.Количество > 0 // Рекомендуемая проверка

Теперь, если Количество = 7, это условие будет истинным для чисел 1, 2, 3, 4, 5, 6 и 7 из таблицы ВТ_РядЧисел. В результате соединения будет создано ровно семь строк, что нам и требовалось.

Полный текст рабочего запроса

Объединим все шаги в один пакетный запрос для наглядности.


// Шаг 1.1: Создаем базовый набор цифр от 0 до 9
ВЫБРАТЬ 0 КАК Числа ПОМЕСТИТЬ РядЧисел
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 1.2: Генерируем таблицу чисел от 1 до 1000
ВЫБРАТЬ
    РядЧисел2.Числа * 100 + РядЧисел1.Числа * 10 + РядЧисел.Числа + 1 КАК Число
ПОМЕСТИТЬ ВТ_РядЧисел
ИЗ
    РядЧисел КАК РядЧисел,
    РядЧисел КАК РядЧисел1,
    РядЧисел КАК РядЧисел2
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Выполняем основную выборку с правильным условием соединения
ВЫБРАТЬ
    ТОРГ_ПеремещениеТовары.Номенклатура,
    1 КАК Количество,
    ТОРГ_ПеремещениеТовары.Цена, // Можно добавить и другие поля
    ТОРГ_ПеремещениеТовары.Сумма / ТОРГ_ПеремещениеТовары.Количество КАК СуммаСтроки // Например, можно рассчитать сумму для одной единицы
ИЗ
    Документ.ТОРГ_Перемещение.Товары КАК ТОРГ_ПеремещениеТовары
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_РядЧисел КАК ВТ_РядЧисел
        ПО ВТ_РядЧисел.Число <= ТОРГ_ПеремещениеТовары.Количество
ГДЕ
    ТОРГ_ПеремещениеТовары.Ссылка = &Ссылка
    И ТОРГ_ПеремещениеТовары.Количество > 0

Дополнительные рекомендации

  1. Работа с дробными числами: Данный метод корректно работает только с целыми значениями в поле Количество. Если количество может быть дробным (например, 2.5), то условие <= все равно создаст только две строки. В этом случае вам нужно заранее определить бизнес-логику: округлять количество (ОКР()) или брать целую часть (ЦЕЛ()) перед использованием в запросе.
  2. Нулевые и отрицательные значения: Чтобы избежать лишних операций и потенциальных ошибок, рекомендуется добавлять в условие ГДЕ фильтр по количеству, например ТОРГ_ПеремещениеТовары.Количество > 0.
  3. Производительность: Генерация очень большой таблицы чисел (на миллионы записей) может быть ресурсоемкой. Всегда оценивайте максимальное возможное количество в ваших данных и создавайте таблицу чисел с небольшим запасом, а не на заведомо избыточный диапазон.

Таким образом, используя пакетный запрос и временную таблицу с рядом чисел, можно эффективно и элегантно решить задачу "размножения" строк, не прибегая к циклам в коде 1С.

← На главную