При разработке алгоритмов сложной обработки и группировки данных в платформе 1С:Предприятие 7.7 часто возникает потребность сохранить в одной строке таблицы значений не просто примитивное значение (число, строку, ссылку на справочник), а целую коллекцию связанных элементов. Разберем особенности платформы 1С 7.7, ограничения встроенных типов данных и практические способы решения этой задачи.
В платформе 1С 7.7 классический массив создается только статически через объявление переменной с указанием размерности: Перем МойМассив[10]. Такой массив не является самостоятельным ссылочным объектом базы данных или среды исполнения, поэтому его невозможно напрямую передать в ячейку таблицы значений. Однако, если колонка таблицы значений создана без строгого указания типа, она способна принимать значения любых типов, включая динамические объекты платформы.
Рассмотрим основные подходы к хранению множества связанных данных в ячейках таблицы значений.
Наиболее удобный объектный способ организовать вложенный набор данных — использовать динамический объект СписокЗначений. Для каждой строки таблицы значений создается свой экземпляр списка, в который добавляются нужные элементы.
Посмотрим на пример программного наполнения таблицы значений:
// Создаем основную таблицу значений
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Код");
ТЗ.НоваяКолонка("Наименование");
ТЗ.НоваяКолонка("ВложенныйСписок"); // Тип колонки не указываем
// Добавляем строку и наполняем вложенный список
ТЗ.НоваяСтрока();
ТЗ.Код = "001";
ТЗ.Наименование = "Основной товар";
// Инициализируем СписокЗначений в ячейке
ТЗ.ВложенныйСписок = СоздатьОбъект("СписокЗначений");
ТЗ.ВложенныйСписок.ДобавитьЗначение("Элемент 1", "Параметр 1");
ТЗ.ВложенныйСписок.ДобавитьЗначение("Элемент 2", "Параметр 2");
Для чтения и обхода данных из такой ячейки достаточно обратиться к свойству строки как к обычному списку:
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
ВложенныйСписок = ТЗ.ВложенныйСписок;
Для Ном = 1 По ВложенныйСписок.РазмерСписка() Цикл
ТекЗначение = ВложенныйСписок.ПолучитьЗначение(Ном);
// Обработка полученного значения
КонецЦикла;
КонецЦикла;
Важное замечание: Метод Сортировать() таблицы значений не может упорядочивать строки по колонкам, содержащим составные объекты (такие как СписокЗначений или вложенная ТаблицаЗначений). Сортировку следует выполнять только по стандартным колонкам с простыми или ссылочными типами.
Если требуется компактное представление данных или их последующая передача/сохранение в строковый реквизит (например, шапки документа), можно использовать внутреннюю сериализацию платформы через функции ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр().
Разберем реализацию этого метода:
// Формируем промежуточный список
Спис = СоздатьОбъект("СписокЗначений");
Спис.ДобавитьЗначение("Исполнитель 1");
Спис.ДобавитьЗначение("Исполнитель 2");
// Помещаем сериализованную строку в ячейку таблицы значений
ТЗ.НоваяСтрока();
ТЗ.ВложенныеДанныеСтрокой = ЗначениеВСтрокуВнутр(Спис);
// При извлечении данных восстанавливаем объект обратно
ВосстановленныйСписок = СоздатьОбъект("СписокЗначений");
ВосстановленныйСписок = ЗначениеИзСтрокиВнутр(ТЗ.ВложенныеДанныеСтрокой);
Если вложенные данные состоят из простых идентификаторов, чисел или коротких строк, наиболее производительным решением будет хранение значений в обычной текстовой строке с использованием разделителя (например, запятой, точки с запятой или символа перевода строки).
Посмотрим на пример разбора строки через функции работы с текстовыми строками платформы:
// Запись данных через разделитель переноса строк
ТЗ.НоваяСтрока();
ТЗ.СписокКодов = "Код1" + РазделительСтрок + "Код2" + РазделительСтрок + "Код3";
// Разбор данных из ячейки
ТекСтрокаДанных = ТЗ.СписокКодов;
КолСтрок = СтрКоличествоСтрок(ТекСтрокаДанных);
Для Индекс = 1 По КолСтрок Цикл
ЗначениеЭлемента = СтрПолучитьСтроку(ТекСтрокаДанных, Индекс);
// Обработка отдельного значения
КонецЦикла;
Когда исходный массив данных очень велик (десятки и сотни тысяч записей), создание отдельного объекта СписокЗначений для каждой строки таблицы значений приведет к повышенному расходу памяти и замедлению работы сборщика мусора 1С 7.7. В этом случае оптимальным решением является переход к плоской (денормализованной) таблице значений.
Разберем по шагам алгоритм работы с плоской таблицей:
ТЗ.Сортировать("ИдентификаторРодителя, Порядок").
// Структура плоской таблицы:
// Колонка 1: ИД_Родителя (Число/Строка)
// Колонка 2: ЗначениеВложенногоЭлемента
ТЗ.Сортировать("ИД_Родителя+");
ТекИД = "";
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
Если ТЗ.ИД_Родителя <> ТекИД Тогда
ТекИД = ТЗ.ИД_Родителя;
// Действия при начале обработки нового родительского блока
КонецЕсли;
// Обработка вложенного элемента
ОбработатьЭлемент(ТЗ.ЗначениеВложенногоЭлемента);
КонецЦикла;
Выбор конкретного варианта зависит от решаемой задачи: для небольших наборов параметров в интерфейсе отлично подходит объект СписокЗначений в нетипизированной колонке, а при масштабных выгрузках и расчетах предпочтительнее использовать строковые разделители или денормализованные таблицы.