Наполнение ТаблицыЗначений: строки, колонки, массовая загрузка и поиск

· 9 мин

Содержание

Чтобы добавить значения в таблицу значений 1С, вызовите НоваяСтрока = ТЗ.Добавить() и присвойте значения колонкам: НоваяСтрока.Товар = Товар. Для вставки в середину служит ТЗ.Вставить(Индекс), для целой колонки — ЗаполнитьЗначения() и ЗагрузитьКолонку(), для тысяч строк — выгрузка результата запроса.

Ниже код для каждого случая, разница в скорости и ошибки, на которых чаще всего спотыкаются при наполнении таблицы. Всё относится к встроенному языку платформы 8.3 и не зависит от конфигурации: БП, ЗУП, УТ или своя разработка.

Подготовка: создаём таблицу и описываем колонки

Строку нельзя добавить, пока у таблицы нет колонок. Сначала создаём объект, потом коллекцию Колонки.

ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Товар", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), "Товар");
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
ТЗ.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));

Параметры Колонки.Добавить() по порядку:

  1. Имя — латиница или кириллица без пробелов, как имя переменной.
  2. Тип — ОписаниеТипов. Можно не указывать.
  3. Заголовок — что увидит пользователь в табличном поле.
  4. Ширина — в символах, для отображения.

Колонка без типа принимает что угодно. Это удобно для черновых расчётов, но мешает в двух местах: такую таблицу нельзя корректно передать параметром в запрос, а в типизированной колонке значение приводится к её типу. Строка «12,5» в числовой колонке станет числом, «абв» — нулём.

Если таблица нужна той же структуры, что уже есть, не описывайте колонки заново: НоваяТЗ = СтараяТЗ.СкопироватьКолонки(); вернёт пустую таблицу с теми же колонками и типами.

Где таблица доступна

Объект ТаблицаЗначений работает на сервере, в толстом клиенте и во внешнем соединении. В тонком и веб-клиенте его нет. В управляемой форме реквизит типа «ТаблицаЗначений» на клиенте превращается в ДанныеФормыКоллекция. Наполнять его проще на сервере:

&НаСервере
Процедура ЗаполнитьНаСервере()
    ТЗ = РеквизитФормыВЗначение("ТаблицаТоваров");
    // ... добавляем строки ...
    ЗначениеВРеквизитФормы(ТЗ, "ТаблицаТоваров");
КонецПроцедуры

На клиенте у коллекции формы тоже есть Добавить(), но методов ЗагрузитьКолонку(), Свернуть() и индексов там нет.

Добавить(): новая строка в конец таблицы

Основной способ добавить значения в таблицу значений — метод Добавить(). Он создаёт пустую строку в конце и возвращает её.

НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Товар = Товар;
НоваяСтрока.Количество = 10;
НоваяСтрока.Сумма = 1250.50;

Значения пишутся через точку по имени колонки или через квадратные скобки, если имя колонки хранится в переменной:

ИмяКолонки = "Комментарий";
НоваяСтрока[ИмяКолонки] = "Срочно";

Заполнение одной строкой кода

Когда полей много, а источник — структура, выборка запроса или другая строка, используйте ЗаполнитьЗначенияСвойств(). Он копирует значения с совпадающими именами и пропускает остальные.

Данные = Новая Структура("Товар, Количество, Сумма", Товар, 5, 600);
ЗаполнитьЗначенияСвойств(ТЗ.Добавить(), Данные);

Третий и четвёртый параметры ограничивают набор: ЗаполнитьЗначенияСвойств(Приемник, Источник, "Товар, Сумма") — только перечисленные, ЗаполнитьЗначенияСвойств(Приемник, Источник, , "Комментарий") — все, кроме перечисленных.

Имена в источнике должны совпадать с именами колонок буква в букву. Если в структуре ключ Кол, а колонка Количество, значение молча не скопируется, ошибки не будет.

Вставить(): строка в нужную позицию

Добавить() всегда пишет в конец. Если строка должна встать, например, второй, нужен Вставить(). Он принимает индекс, сдвигает строки ниже и возвращает новую строку.

// Индексы строк начинаются с 0. Вставка на место второй строки:
НоваяСтрока = ТЗ.Вставить(1);
НоваяСтрока.Товар = Товар;
НоваяСтрока.Комментарий = "Вставлено вручную";

Индекс должен лежать в пределах существующих строк: от 0 до ТЗ.Количество() - 1. Для вставки в конец используйте Добавить(), это нагляднее и не требует расчёта номера.

Вставка перед найденной строкой

Обычно позицию не знают заранее, а ищут строку-ориентир. Номер даёт метод Индекс():

Ориентир = ТЗ.Найти(Товар, "Товар");
Если Ориентир <> Неопределено Тогда
    НоваяСтрока = ТЗ.Вставить(ТЗ.Индекс(Ориентир));
    НоваяСтрока.Товар = ДругойТовар;
КонецЕсли;

Перестановка уже добавленных строк

Чтобы не удалять и не вставлять строку заново, двигайте её методом Сдвинуть(Строка, Смещение). Положительное смещение — вниз, отрицательное — вверх:

ТЗ.Сдвинуть(НоваяСтрока, -1); // на одну позицию выше

Если порядок задаётся данными, проще добавить всё подряд и вызвать ТЗ.Сортировать("Сумма Убыв, Товар").

Как добавить колонку и значения в существующую таблицу

Бывает, что строки уже есть, а значения нужно дописать в новую колонку. Тут три приёма.

Новая колонка. Колонки.Добавить() работает и на заполненной таблице: у всех строк появится пустое значение. Чтобы колонка встала не последней, а, скажем, первой, есть ТЗ.Колонки.Вставить(0, "НомерПП", Новый ОписаниеТипов("Число")).

Одно значение во все строки. Метод ЗаполнитьЗначения():

ТЗ.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
ТЗ.ЗаполнитьЗначения(ОсновнойСклад, "Склад");

Вторым параметром можно передать несколько колонок через запятую. Без второго параметра значение запишется во все колонки, обычно это не то, что нужно.

Разные значения из массива. ЗагрузитьКолонку(Массив, Колонка) раскладывает элементы массива по строкам по порядку: первый элемент в первую строку и так далее.

Номера = Новый Массив;
Для Сч = 1 По ТЗ.Количество() Цикл
    Номера.Добавить(Сч);
КонецЦикла;
ТЗ.ЗагрузитьКолонку(Номера, "НомерПП");

Обратная операция ВыгрузитьКолонку("Товар") вернёт массив значений колонки. Связка «выгрузить → обработать массив → загрузить обратно» часто быстрее, чем обход строк с присваиванием.

Если в массиве больше элементов, чем строк в таблице, новые строки не появятся. ЗагрузитьКолонку() пишет только в существующие строки, поэтому сначала создайте нужное количество строк.

Массовое добавление значений из других источников

Когда строк сотни и тысячи, построчное заполнение пишет много кода и работает медленнее. Выбирайте способ по источнику данных.

Из запроса

Самый быстрый и короткий вариант: таблица создаётся сразу с колонками и типами из запроса.

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Остатки.Номенклатура КАК Товар,
|    Остатки.КоличествоОстаток КАК Количество
|ИЗ
|    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки";
ТЗ = Запрос.Выполнить().Выгрузить();

Имя регистра здесь для примера, подставьте свой объект метаданных. Псевдонимы через КАК становятся именами колонок, поэтому сразу называйте поля так, как они нужны дальше.

Из другой таблицы значений

Если структуры разные, переносите построчно с ЗаполнитьЗначенияСвойств():

Для Каждого СтрокаИсточника Из ТЗИсточник Цикл
    ЗаполнитьЗначенияСвойств(ТЗ.Добавить(), СтрокаИсточника);
КонецЦикла;

Если нужна копия целиком или часть строк, есть Скопировать(). Первый параметр — массив строк или структура отбора, второй — список колонок:

Отбор = Новая Структура("Склад", ОсновнойСклад);
ТЗСклада = ТЗ.Скопировать(Отбор, "Товар, Количество");

Из массива структур и из табличной части

Массив структур обходят тем же циклом с ЗаполнитьЗначенияСвойств(). Табличную часть документа выгружают одной строкой: ТЗ = ДокументОбъект.Товары.Выгрузить();. Обратно — ДокументОбъект.Товары.Загрузить(ТЗ), колонки сопоставятся по именам.

Сразу со свёрткой

Если после добавления нужно сложить суммы по одинаковым товарам, не ищите дубли вручную. Добавьте всё подряд, затем:

ТЗ.Свернуть("Товар", "Количество, Сумма");

Колонки, которые не попали ни в группировки, ни в суммирование, после свёртки пропадут.

Индексы и поиск строк перед добавлением

Частая задача: добавить значение, только если такой строки ещё нет, иначе увеличить количество. Без индекса каждая проверка просматривает всю таблицу.

ТЗ.Индексы.Добавить("Товар");

Для Каждого Позиция Из Поступления Цикл
    Найденная = ТЗ.Найти(Позиция.Товар, "Товар");
    Если Найденная = Неопределено Тогда
        Найденная = ТЗ.Добавить();
        Найденная.Товар = Позиция.Товар;
    КонецЕсли;
    Найденная.Количество = Найденная.Количество + Позиция.Количество;
КонецЦикла;

Методы поиска:

  • 🔎 Найти(Значение, "Колонки") — первая строка с этим значением или Неопределено.
  • 📋 НайтиСтроки(Структура) — массив всех строк, где совпали все поля структуры. Пустой массив, если ничего нет.
  • 🔢 Индекс(Строка) — номер строки, нужен для Вставить() и Получить().

Индекс по нескольким колонкам задают одной строкой: ТЗ.Индексы.Добавить("Товар, Склад"). Он помогает НайтиСтроки(), когда структура отбора содержит те же поля.

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

Индекс поддерживается автоматически: новые строки, добавленные через Добавить() и Вставить(), сразу попадают в него. Перестраивать вручную ничего не нужно.

Какой способ наполнения выбрать

ЗадачаМетодСкоростьЧто учесть
Одна-две строкиДобавить() + присваиваниевысокаясамый читаемый код
Строка в серединуВставить(Индекс)высокаяиндексы с нуля
Много полей из структуры или выборкиЗаполнитьЗначенияСвойств()высокаяимена должны совпадать
Одно значение во все строкиЗаполнитьЗначения()очень высокаяукажите колонку, иначе заполнит все
Разные значения в колонкуЗагрузитьКолонку()очень высокаястроки должны уже существовать
Данные из базыЗапрос.Выполнить().Выгрузить()максимальнаяколонки берутся из псевдонимов
Часть другой таблицыСкопировать(Отбор, Колонки)высокаясоздаётся новый объект
Добавить без дублейНайти() + индекс + Добавить()зависит от индексаиндекс до цикла

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

Обратное направление: таблица значений в запрос

Иногда таблицу наполняют в коде, а потом нужно соединить её с данными базы. Передайте её параметром и поместите во временную таблицу:

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|    Т.Товар КАК Товар,
|    Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТТовары
|ИЗ
|    &ТаблицаТоваров КАК Т";
Запрос.УстановитьПараметр("ТаблицаТоваров", ТЗ);
Запрос.Выполнить();

Для этого у каждой колонки должен быть явно задан тип. Колонка без типа вызовет ошибку при выполнении запроса.

Типичные ошибки при добавлении значений

Диагностика: выберите симптом

Ещё одна ловушка — изменение таблицы внутри обхода этой же таблицы. Не добавляйте и не удаляйте строки в цикле Для Каждого Строка Из ТЗ: соберите новые строки в отдельную таблицу или массив, а затем перенесите их после цикла. Для удаления используйте обратный цикл по индексу:

Всего = ТЗ.Количество();
Для Сч = 1 По Всего Цикл
    Строка = ТЗ[Всего - Сч];
    Если Строка.Количество = 0 Тогда
        ТЗ.Удалить(Строка);
    КонецЕсли;
КонецЦикла;

Частые вопросы

Как добавить в таблицу значений сразу несколько пустых строк?
Вызовите Добавить() в цикле нужное число раз. Отдельного метода «добавить N строк» у таблицы значений нет.
Чем таблица значений отличается от табличной части при добавлении строк?
Синтаксис тот же: Добавить(), Вставить(). Но табличная часть принадлежит объекту и сохраняется вместе с ним, а таблица значений живёт только в памяти до конца процедуры или сеанса.
Можно ли хранить в колонке таблицы значений другую таблицу?
Да, если тип колонки не задан или в описание типов входит ТаблицаЗначений. Так строят вложенные структуры, но передать такую таблицу в запрос уже не получится.
Почему добавленные на сервере строки не видны в форме?
Таблица, полученная через РеквизитФормыВЗначение(), — отдельная копия. После наполнения верните её в форму вызовом ЗначениеВРеквизитФормы(), иначе изменения пропадут при возврате на клиент.
Как объединить две таблицы с одинаковыми колонками?
Обойдите вторую таблицу циклом и для каждой строки вызовите ЗаполнитьЗначенияСвойств(ТЗ1.Добавить(), Строка). Готового метода «объединить» у таблицы значений нет; альтернатива — запрос с ОБЪЕДИНИТЬ ВСЕ по двум временным таблицам.
Как очистить таблицу и заполнить заново, сохранив колонки?
Метод Очистить() удаляет все строки, а колонки и индексы остаются. После этого снова добавляйте строки обычным образом.