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

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникают задачи, когда одну запись в базе данных (например, остаток товара в количестве 5 штук) необходимо представить в виде нескольких строк (5 записей по 1 штуке). Такая потребность чаще всего продиктована требованиями маркировки («Честный ЗНАК»), учетом бланков строгой отчетности (БСО) или необходимостью распределения штрихкодов упаковок. Разберем подробно, как реализовать это «размножение» строк непосредственно в запросе и с помощью программных методов.

Метод 1: Использование вспомогательной таблицы чисел (Классический подход)

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

Проанализируем шаги реализации этого метода:

  1. Создадим временную таблицу, содержащую порядковые номера.
  2. Выполним ВНУТРЕННЕЕ СОЕДИНЕНИЕ таблицы товаров с таблицей чисел по условию Числа.Номер <= Товары.Количество.

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


// Шаг 1: Создаем базовые цифры
ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ Цифры ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2 
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5 
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9;

// Шаг 2: Генерируем последовательность до 1000 (перекрестное соединение)
ВЫБРАТЬ
    (Т1.Цифра + Т10.Цифра * 10 + Т100.Цифра * 100 + 1) КАК Номер
ПОМЕСТИТЬ ТаблицаЧисел
ИЗ
    Цифры КАК Т1,
    Цифры КАК Т10,
    Цифры КАК Т100;

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

В данном примере мы использовали перекрестное соединение (декартово произведение) таблицы из 10 цифр самой с собой трижды, что дало нам последовательность от 1 до 1000. Этого достаточно, если максимальное количество товара в одной строке не превышает тысячи.

Метод 2: Программная подготовка таблицы чисел

Если мы не знаем заранее максимальный остаток или не хотим перегружать запрос генерацией чисел, мы можем передать таблицу чисел как параметр запроса. Это особенно удобно, если расчет ведется во внешней обработке.

Разберем последовательность действий программиста:


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

// Заполняем её до максимально возможного значения остатка в выборке
МаксОстаток = 5000; // Можно вычислить динамически
Для н = 1 По МаксОстаток Цикл
    ТЗ_Числа.Добавить().Номер = н;
КонецЦикла;

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

Этот метод считается более гибким и безопасным с точки зрения производительности, так как индекс в ТаблицеЗначений (если его добавить) позволит СУБД быстрее выполнить соединение.

Работа с дробным количеством и особенности маркировки

Проанализируем ситуацию, упомянутую в обсуждении: что делать, если количество дробное (например, 2.5 кг)? Стандартное соединение Число <= Количество для числа 3 выдаст ложь, и мы получим только 2 строки. Если задача требует вывода всех вхождений, включая дробный остаток, логику ВЫБОР нужно усложнить.

Рассмотрим вариант решения для весового товара:


ВЫБРАТЬ
    Остатки.Номенклатура,
    ВЫБОР
        КОГДА Числа.Номер < Остатки.Количество ТОГДА 1
        ИНАЧЕ Остатки.Количество - (Числа.Номер - 1)
    КОНЕЦ КАК ВесСтроки
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаЧисел КАК Числа
        ПО Числа.Номер <= Остатки.Количество + 0.999 // Округляем вверх для захвата последней строки

Важно: При работе с маркировкой (ГИС МТ «Честный ЗНАК») часто требуется не просто размножить строки, а сопоставить их с конкретными кодами маркировки. В этом случае вместо генерации абстрактных чисел лучше использовать таблицу самих кодов, загруженную из Excel или считанную сканером, и соединять её с данными о товаре.

Ограничения и рекомендации

Выясним причины, по которым к этому методу стоит относиться с осторожностью:

Таким образом, мы рассмотрели основные способы «размножения» строк. Наиболее стабильным для 1С 8.3 является метод с предварительно созданной временной таблицей чисел, который позволяет решать задачи любой сложности без выхода за рамки языка запросов.

← На главную