При разработке сложных отчетов, пакетных обработок и выгрузок данных в платформе 1С 7.7 часто возникает необходимость сформировать динамическое количество коллекций данных — например, отдельную таблицу значений для каждого документа или контрагента. Начинающие разработчики нередко пытаются динамически объявлять переменные или использовать конструкции вида Перем ТЗ[N], что приводит к синтаксическим ошибкам или непредсказуемому поведению программы. Разберем по шагам, какие существуют ограничения платформы и какими способами можно правильно создавать и адресовать N таблиц значений.
Прежде чем перейти к практическим решениям, проанализируем базовые особенности встроенного языка 1С:Предприятия 7.7:
Перем должна быть строго константным числовым литералом (например, Перем МассивТЗ[100]). Задать размер массива через переменную (например, Перем МассивТЗ[Количество]) на этапе компиляции модуля невозможно.ТЗ_1, ТЗ_2. Для адресации по именам или индексам необходимо использовать специализированные контейнеры.Если максимальное количество таблиц заранее известно или ограничено разумным числом, простейшим решением является объявление массива. Рассмотрим пример:
Процедура СформироватьМассивТЗ()
Перем МассивТЗ[100];
Для к = 1 По 10 Цикл
МассивТЗ[к] = СоздатьОбъект("ТаблицаЗначений");
МассивТЗ[к].НоваяКолонка("Идентификатор");
МассивТЗ[к].НоваяКолонка("Сумма");
МассивТЗ[к].НоваяСтрока();
МассивТЗ[к].Идентификатор = к;
МассивТЗ[к].Сумма = к * 100;
КонецЦикла;
// Обращение к конкретной таблице по индексу:
МассивТЗ[1].ВыбратьСтроку(,"Первая таблица");
КонецПроцедуры
Важный момент: объявление Перем задает жесткий верхний предел. Если в процессе работы понадобится 101-я таблица, программа выдаст ошибку выхода за границы массива.
Наиболее гибкий и распространенный способ — создание структуры «таблица в таблице». В качестве контейнера используется одна главная таблица значений, у которой одна из колонок имеет тип ТаблицаЗначений. Рассмотрим подробнее код создания и наполнения такой структуры на примере выборки документов:
Процедура ОбработатьДокументы()
ГлавнаяТЗ = СоздатьОбъект("ТаблицаЗначений");
ГлавнаяТЗ.НоваяКолонка("Документ", "Документ");
ГлавнаяТЗ.НоваяКолонка("ТабличнаяЧасть", "ТаблицаЗначений");
ВыборкаДок = СоздатьОбъект("Документ.Реализация");
ВыборкаДок.ВыбратьДокументы(НачМесяца(ТекущаяДата()), КонМесяца(ТекущаяДата()));
Пока ВыборкаДок.ПолучитьДокумент() = 1 Цикл
ГлавнаяТЗ.НоваяСтрока();
ГлавнаяТЗ.Документ = ВыборкаДок.ТекущийДокумент();
// Обязательно создаем новый объект на каждой итерации:
ГлавнаяТЗ.ТабличнаяЧасть = СоздатьОбъект("ТаблицаЗначений");
ВыборкаДок.ВыгрузитьТабличнуюЧасть(ГлавнаяТЗ.ТабличнаяЧасть);
КонецЦикла;
// Обход полученных вложенных таблиц:
ГлавнаяТЗ.ВыбратьСтроки();
Пока ГлавнаяТЗ.ПолучитьСтроку() = 1 Цикл
ВложеннаяТЗ = ГлавнаяТЗ.ТабличнаяЧасть;
// Работаем с ВложеннаяТЗ как с обычной таблицей значений
КонецЦикла;
КонецПроцедуры
Когда требуется обращаться к созданным таблицам значений не по числовому индексу, а по текстовому строковому представлению (имени), удобно использовать СписокЗначений. Метод ДобавитьЗначение() принимает сам объект и строковое представление:
Процедура СоздатьТаблицыВХранилище()
ХранилищеТЗ = СоздатьОбъект("СписокЗначений");
КоличествоТЗ = 5;
Для ы = 1 По КоличествоТЗ Цикл
ИмяТЗ = "Таблица_" + СокрЛП(ы);
ВременнаяТЗ = СоздатьОбъект("ТаблицаЗначений");
ВременнаяТЗ.НоваяКолонка("Параметр");
ВременнаяТЗ.НоваяСтрока();
ВременнаяТЗ.Параметр = "Значение " + СокрЛП(ы);
ХранилищеТЗ.ДобавитьЗначение(ВременнаяТЗ, ИмяТЗ);
КонецЦикла;
// Получение таблицы по индексу из списка:
Для Позиция = 1 По ХранилищеТЗ.РазмерСписка() Цикл
ИмяТаблицы = "";
ТекущаяТЗ = ХранилищеТЗ.ПолучитьЗначение(Позиция, ИмяТаблицы);
Сообщить("Обработка объекта с именем: " + ИмяТаблицы);
КонецЦикла;
КонецПроцедуры
Если критически важно обращаться к N таблицам напрямую по произвольному текстовому ключу без перебора списка, наилучшим решением выступает системный ассоциативный массив Scripting.Dictionary:
Процедура СоздатьСловарьТаблиц()
СловарьТЗ = СоздатьОбъект("Scripting.Dictionary");
Для Индекс = 1 По 10 Цикл
Ключ = "ТЗ_Склад_" + СокрЛП(Индекс);
НоваяТЗ = СоздатьОбъект("ТаблицаЗначений");
НоваяТЗ.НоваяКолонка("Номенклатура");
НоваяТЗ.НоваяКолонка("Остаток");
СловарьТЗ.Add(Ключ, НоваяТЗ);
КонецЦикла;
// Прямой доступ по имени без циклов:
Если СловарьТЗ.Exists("ТЗ_Склад_3") = 1 Тогда
НужнаяТЗ = СловарьТЗ.Item("ТЗ_Склад_3");
НужнаяТЗ.НоваяСтрока();
КонецЕсли;
КонецПроцедуры
Выясним причину частой ошибки, с которой сталкиваются разработчики. При попытке быстро размножить таблицы с помощью группового метода:
// ОШИБОЧНЫЙ КОД — ПРИВОДИТ К ПАДЕНИЮ ПРОГРАММЫ (CRASH 1cv7s.exe)
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("КолонкаТЗ", "ТаблицаЗначений");
ТЗ.КоличествоСтрок(10);
ТЗ.Заполнить(СоздатьОбъект("ТаблицаЗначений"), 1, 10, "КолонкаТЗ");
Проанализируем ситуацию: метод Заполнить() в 1С 7.7 на уровне движка C++ спроектирован исключительно для примитивных типов (числа, строки, даты). При передаче в него агрегатного типа (таблицы значений или списка) происходит сбой в обработке счетчиков ссылок COM-объектов, что вызывает нарушение адресации памяти и аварийное закрытие платформы без вывода сообщений об ошибке.
Правильный подход: создание нового экземпляра таблицы значений через СоздатьОбъект("ТаблицаЗначений") должно выполняться строго внутри цикла на каждой итерации для каждой отдельной строки.
В платформе 1С 7.7 сборщик мусора очищает память на основе подсчета ссылок. Пока родительская коллекция (массив, список или главная ТЗ) удерживает ссылки на созданные дочерние таблицы значений, они не будут удалены из оперативной памяти. При обработке больших массивов данных перед завершением работы процедуры рекомендуется вызывать метод Очистить() для родительской таблицы значений или списка, чтобы предотвратить утечки памяти при повторных запусках обработок.