Как использовать временную табличную часть для расчетов в общем модуле 1С 8.3?

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

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

Основная проблема, с которой мы сталкиваемся, заключается в желании использовать структуру существующей табличной части документа (например, Товары из РасходнаяНакладная) без необходимости вручную описывать каждую колонку с ее типом и свойствами. Это особенно актуально, если табличная часть содержит множество полей или ее структура может измениться в будущем.

Подход 1: Создание объекта документа без записи

Изначально автор предложил довольно оригинальное решение: создать полноценный объект документа, заполнить его табличную часть необходимыми данными для расчетов, выполнить вычисления, а затем просто не записывать этот документ в базу данных. Такой подход привлекателен своей простотой, поскольку позволяет использовать уже существующую структуру табличной части без дополнительного кодирования.

Давайте проанализируем этот подход с точки зрения его корректности и влияния на систему:

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

Пример кода (концептуальный):


// В общем модуле, используемом регламентным заданием
Функция ВыполнитьРасчетыСВременнойТЧ()

    // Создаем новый объект документа в памяти
    Док = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
    
    // Получаем табличную часть для заполнения
    ВременнаяТЧ = Док.Товары;
    
    // Заполняем ВременнаяТЧ необходимыми данными
    // Например, добавим несколько строк
    НоваяСтрока = ВременнаяТЧ.Добавить();
    НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Товар 1");
    НоваяСтрока.Количество = 10;
    НоваяСтрока.Цена = 100;
    НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;

    НоваяСтрока = ВременнаяТЧ.Добавить();
    НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Товар 2");
    НоваяСтрока.Количество = 5;
    НоваяСтрока.Цена = 250;
    НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
    
    // Выполняем необходимые расчеты, используя данные из ВременнаяТЧ
    ОбщаяСумма = 0;
    Для Каждого СтрокаТЧ Из ВременнаяТЧ Цикл
        ОбщаяСумма = ОбщаяСумма + СтрокаТЧ.Сумма;
    КонецЦикла;
    
    Сообщить("Общая сумма товаров для расчета: " + ОбщаяСумма);
    
    // Док не записывается в базу данных.
    // После завершения выполнения функции, Док и ВременнаяТЧ будут автоматически удалены из памяти.

    Возврат ОбщаяСумма;
КонецФункции;

Вывод по подходу: Хотя этот метод технически работоспособен и не приводит к проблемам с базой данных, он может быть менее явным и понятным для других разработчиков. С точки зрения "чистоты" кода и архитектуры, обычно предпочтительнее использовать объекты, специально предназначенные для временного хранения данных.

Подход 2: Использование ТаблицыЗначений (ТЗ)

ТаблицаЗначений является наиболее распространенным и явно предназначенным для работы с временными табличными данными в 1С. Этот программный объект существует исключительно в оперативной памяти и позволяет программно создавать произвольные наборы данных.

Разберем основные принципы и способы использования ТаблицыЗначений:

  1. Принцип работы: ТаблицаЗначений – это коллекция, похожая на таблицу в базе данных, но существующая только в памяти. Вы можете описывать колонки (указывая их имя, тип и заголовок), добавлять, редактировать, удалять и сортировать строки. Она не привязана напрямую к метаданным конфигурации (за исключением типов колонок) и не взаимодействует с базой данных. Память, занимаемая ТаблицейЗначений, также автоматически освобождается системой 1С.
  2. Создание структуры ТЗ из существующей табличной части документа: Для решения нашей задачи, когда нам нужна структура, полностью аналогичная ТЧ документа, мы можем получить ее с помощью метода Выгрузить() от пустой ссылки на табличную часть.

Рассмотрим примеры:

Пример 1: Получение структуры ТЗ из пустой ссылки на табличную часть.

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


// В общем модуле
Функция ПолучитьВременнуюТЧКакТЗ_ПустаяСсылка()

    // Создаем пустую ссылку на документ "РеализацияТоваровУслуг"
    ПустаяСсылкаДокумента = Документы.РеализацияТоваровУслуг.ПустаяСсылка();
    
    // Получаем табличную часть "Товары" из этой пустой ссылки
    // и выгружаем ее структуру (с пустыми данными) в ТаблицуЗначений
    ВременнаяТЧ_КакТЗ = ПустаяСсылкаДокумента.Товары.Выгрузить();
    
    Сообщить("Получена ТаблицаЗначений со структурой ТЧ Товары. Количество колонок: " + ВременнаяТЧ_КакТЗ.Колонки.Количество());
    
    // Далее ВременнаяТЧ_КакТЗ можно заполнять и использовать для расчетов
    НоваяСтрока = ВременнаяТЧ_КакТЗ.Добавить();
    НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Тестовый Товар");
    НоваяСтрока.Количество = 7;
    НоваяСтрока.Цена = 120;
    // ... и так далее для других колонок
    
    Возврат ВременнаяТЧ_КакТЗ;

КонецФункции;

Пример 2: Получение структуры и данных ТЗ из запроса.

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


// В общем модуле
Функция ПолучитьВременнуюТЧКакТЗ_ИзЗапроса()

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
        |    РеализацияТоваровУслугТовары.Количество КАК Количество,
        |    РеализацияТоваровУслугТовары.Цена КАК Цена,
        |    РеализацияТоваровУслугТовары.Сумма КАК Сумма
        |ИЗ
        |    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        |ГДЕ
        |    ИСТИНА // Здесь может быть любое условие выборки, или мы просто выбираем структуру
        |
        |УПОРЯДОЧИТЬ ПО
        |    Номенклатура
        |";
        
    // Если нам нужна только структура, но без данных, можно добавить ЛОЖНОЕ условие
    // Запрос.Текст = Запрос.Текст + " ГДЕ ЛОЖЬ";
    
    // Выполняем запрос и выгружаем результат в ТаблицуЗначений
    ВременнаяТЧ_КакТЗ = Запрос.Выполнить().Выгрузить();
    
    Сообщить("Получена ТаблицаЗначений из запроса. Количество строк: " + ВременнаяТЧ_КакТЗ.Количество());
    
    Возврат ВременнаяТЧ_КакТЗ;

КонецФункции;

Обратите внимание, что если нам нужна только структура, но без данных, мы можем добавить к запросу условие ГДЕ ЛОЖЬ. В этом случае запрос вернет пустой результат, но с определенной структурой колонок.

Подход 3: Прямое создание объекта "ДокументТабличнаяЧасть"

Один из самых элегантных способов, предложенных в обсуждении, позволяет создать объект типа ТабличнаяЧасть напрямую, без создания родительского объекта документа. Этот подход идеален, когда вам нужна коллекция, которая по структуре точно соответствует определенной табличной части документа из метаданных.

Разберем этот метод:

  1. Принцип работы: Вы можете использовать конструктор Новый() с полным именем типа "ДокументТабличнаяЧасть.<ИмяДокумента>.<ИмяТабличнойЧасти>". Созданный объект будет являться коллекцией строк, каждая из которых имеет поля, соответствующие колонкам определенной табличной части. Этот объект существует независимо от объекта-владельца (документа) в оперативной памяти.

Пример кода:


// В общем модуле
Функция ПолучитьВременнуюТЧНапрямую()

    // Создаем объект ТабличнаяЧасть, соответствующий структуре ТЧ Товары 
    // документа "РасходнаяНакладная" (или "РеализацияТоваровУслуг" в УТ 10.3)
    Таблица = Новый ("ДокументТабличнаяЧасть.РеализацияТоваровУслуг.Товары");
    
    Сообщить("Создан объект типа ДокументТабличнаяЧасть. Количество строк (пока 0): " + Таблица.Количество());
    
    // Теперь Таблица готова к заполнению, как любая другая табличная часть
    НоваяСтрока = Таблица.Добавить();
    НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Продукт А");
    НоваяСтрока.Количество = 20;
    НоваяСтрока.Цена = 50;
    НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;

    НоваяСтрока = Таблица.Добавить();
    НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Продукт Б");
    НоваяСтрова.Количество = 15;
    НоваяСтрока.Цена = 90;
    НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
    
    // Выполняем расчеты
    СуммаПоТЧ = 0;
    Для Каждого СтрТЧ Из Таблица Цикл
        СуммаПоТЧ = СуммаПоТЧ + СтрТЧ.Сумма;
    КонецЦикла;
    Сообщить("Сумма по временной табличной части: " + СуммаПоТЧ);
    
    Возврат Таблица;

КонецФункции;

Сравнение с ТаблицейЗначений:

Подход 4: Временные таблицы в запросах

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

Рассмотрим, как это работает:

  1. Принцип работы: Временные таблицы – это объекты, которые создаются и существуют в рамках выполнения одного запроса. Они хранятся в оперативной памяти СУБД (в буферном кэше) или, если объем данных слишком велик, могут быть временно записаны на диск (обычно в специальную базу данных TempDB). После завершения выполнения запроса эти временные таблицы автоматически удаляются.
  2. Преимущества: Использование временных таблиц значительно повышает производительность сложных запросов, позволяя разбить их на логические части, индексировать промежуточные результаты и выполнять сложные соединения и агрегации, которые были бы неэффективны в одном большом запросе. Это особенно полезно при работе с большими базами данных.
  3. Применение: Мы можем использовать временные таблицы для предварительной обработки данных, фильтрации, группировки перед окончательным получением результата. Например, сначала выбрать все нужные товары по определенному критерию во временную таблицу, затем присоединить к ним информацию о ценах из другой таблицы, а уже потом выполнить итоговые расчеты.

Пример использования временных таблиц в запросе:


// В общем модуле
Функция ВыполнитьРасчетыСВременнымиТаблицамиЗапроса()

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

    // Устанавливаем параметры запроса
    Запрос.УстановитьПараметр("Склад", Справочники.Склады.НайтиПоНаименованию("Основной склад"));
    Запрос.УстановитьПараметр("ТипЦен", Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Розничная"));

    // Выполняем запрос
    РезультатЗапроса = Запрос.Выполнить();
    
    // Выгружаем результат в ТаблицуЗначений для дальнейшей обработки в коде
    ИтоговаяТаблица = РезультатЗапроса.Выгрузить();
    
    ОбщаяСуммаПоОстаткам = ИтоговаяТаблица.Итог("Сумма");
    Сообщить("Общая сумма товаров на складе по розничным ценам: " + ОбщаяСуммаПоОстаткам);

    Возврат ИтоговаяТаблица;

КонецФункции;

В этом примере мы сначала получаем остатки товаров на складе во временную таблицу ВремТабОстатки, а затем вторым запросом соединяем эти остатки с ценами, вычисляем сумму и получаем окончательный результат.

Подход 5: Механизм временного хранилища

Этот механизм, хотя и не предназначен напрямую для создания "временных табличных частей" для расчетов, может быть полезен, если результаты наших вычислений, представленные в виде ТаблицыЗначений или другого объекта, необходимо передать между различными сеансами, например, из фонового (регламентного) задания в клиентский сеанс, который его инициировал.

Рассмотрим его особенности:

  1. Принцип работы: Вы помещаете любое значение (например, готовую ТаблицуЗначений) во временное хранилище и получаете уникальный адрес этого значения. По этому адресу вы можете затем получить сохраненные данные из другого сеанса или в другом контексте выполнения. Время жизни данных во временном хранилище может быть контролируемым, например, привязанным к форме или имеющим определенный период истечения.
  2. Применение: Используется, когда нужно передать большой объем данных между клиентской и серверной частью без пересылки по сети каждого элемента, или, как в контексте регламентных заданий, передать результаты выполнения фонового задания обратно в основной сеанс для отображения пользователю. Для чисто серверных расчетов в рамках одного вызова общего модуля этот механизм обычно избыточен.

Пример использования временного хранилища (концептуальный):


// В модуле регламентного задания
Функция ВыполнитьДлительныеРасчетыИПередатьРезультат()

    // Предположим, мы получили некую ТаблицуЗначений с результатами расчетов
    ВременнаяТЧ_КакТЗ = Новый ТаблицаЗначений;
    ВременнаяТЧ_КакТЗ.Колонки.Добавить("Код");
    ВременнаяТЧ_КакТЗ.Колонки.Добавить("Описание");
    // ... заполняем ТЗ данными
    
    // Помещаем ТаблицуЗначений во временное хранилище
    АдресВременногоХранилища = ПоместитьВоВременноеХранилище(ВременнаяТЧ_КакТЗ, УникальныйИдентификатор); 
    // УникальныйИдентификатор может быть передан обратно клиенту или записан в регистр
    
    Возврат АдресВременногоХранилища;

КонецФункции;

// В клиентском или серверном модуле, который должен получить результат
// Например, после завершения фонового задания
Функция ПолучитьРезультатИзХранилища(АдресВХ)

    // Получаем ТаблицуЗначений по адресу из временного хранилища
    ПолученнаяТЗ = ПолучитьИзВременногоХранилища(АдресВХ);
    
    Если ПолученнаяТЗ <> Неопределено Тогда
        Сообщить("Результаты расчетов получены из временного хранилища. Количество строк: " + ПолученнаяТЗ.Количество());
        // ... дальнейшая работа с полученной ТЗ
    Иначе
        Сообщить("Данные по адресу " + АдресВХ + " не найдены или устарели.");
    КонецЕсли;

КонецФункции;

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

Заключение и рекомендации

Мы рассмотрели несколько подходов к работе с временными табличными данными в 1С 8.3, каждый из которых имеет свои особенности и области применения:

  1. Создание объекта документа без записи: Технически возможно и не вредит базе данных, но считается менее явным и менее предпочтительным с точки зрения архитектуры.
  2. ТаблицаЗначений (ТЗ): Это наиболее рекомендуемый и стандартный способ работы с временными табличными данными в памяти. Он явный, гибкий и не имеет побочных эффектов. Структуру можно получить из пустой ссылки на ТЧ документа или из запроса.
  3. Прямое создание объекта "ДокументТабличнаяЧасть": Очень удобный и чистый способ, когда вам требуется точная структура определенной табличной части из метаданных без создания всего документа. Является отличной альтернативой ТаблицеЗначений в этом конкретном случае.
  4. Временные таблицы в запросах: Идеально подходят для оптимизации сложных запросов и обработки больших объемов данных на уровне СУБД.
  5. Механизм временного хранилища: Полезен для передачи больших объемов данных между различными сеансами или фоновыми заданиями.

Для решения задачи, описанной в исходном сообщении (расчеты в общем модуле, использование структуры ТЧ документа, без записи), предпочтительными и наиболее "чистыми" являются подходы с использованием ТаблицаЗначений или прямого создания объекта ДокументТабличнаяЧасть. Они обеспечивают необходимую гибкость, не взаимодействуют с базой данных и явно демонстрируют временный характер данных.

← На главную