Добавление колонок в таблицу значений: методы и примеры кода

· 9 мин

Содержание

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

Задача возникает постоянно: собираете таблицу в обработке или отчёте, и по ходу выясняется, что нужно ещё одно поле — расчётная сумма, флаг, колонка под каждый месяц периода. Ниже — все рабочие способы с проверенным кодом: от минимального вызова до динамического создания десятков колонок и заполнения значений без переспама по строкам.

Весь код написан для встроенного языка 1С:Предприятие 8.3 и работает одинаково на тонком и толстом клиенте, в общих модулях и модулях объектов.

Метод Добавить: минимальный вызов и его параметры

Полная сигнатура такая:

Таблица.Колонки.Добавить(<Имя>, <Тип>, <Заголовок>, <Ширина>)

Обязателен только первый параметр — имя. Остальные можно не передавать:

Таблица = Новый ТаблицаЗначений;

// минимум — только имя, колонка принимает любой тип
Таблица.Колонки.Добавить("Комментарий");

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

// имя, тип, заголовок, ширина — для вывода на форму
Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"), "Сумма, руб.", 12);

Разберём параметры:

  • 🏷️ Имя — программный идентификатор колонки. По нему вы обращаетесь к значению в коде: Строка.Сумма. Правила как у имён переменных: буквы, цифры и подчёркивание, без пробелов, не начинается с цифры.
  • 🔤 Тип — объект ОписаниеТипов. Если не передать, колонка будет универсальной и примет значение любого типа.
  • 📝 Заголовок — текст, который видит пользователь, когда таблица выведена на форму или в табличный документ. Здесь пробелы и знаки допустимы.
  • 📐 Ширина — ширина колонки в символах при выводе. На логику кода не влияет.

Метод Добавить возвращает созданный объект — КолонкаТаблицыЗначений. Это удобно, когда свойств много:

НоваяКолонка = Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
НоваяКолонка.Заголовок = "Кол-во, шт";
НоваяКолонка.Ширина = 8;

У объекта колонки доступны для чтения и записи Имя, Заголовок, Тип, Ширина. Значит, уже созданную колонку можно позже переименовать или сменить ей заголовок, не пересоздавая.

Какой тип задать через ОписаниеТипов

Тип колонки описывает объект ОписаниеТипов. В простом случае достаточно строки с названием типа:

Новый ОписаниеТипов("Число")
Новый ОписаниеТипов("Строка")
Новый ОписаниеТипов("Дата")
Новый ОписаниеТипов("Булево")
Новый ОписаниеТипов("СправочникСсылка.Контрагенты")

Можно разрешить несколько типов сразу — перечислите их через запятую:

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

Для чисел, строк и дат стоит задавать квалификаторы — они ограничивают разрядность и длину. Это не формальность: от квалификаторов зависит, как значение ляжет в систему компоновки данных и как округлится при присваивании.

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

// строка ограниченной длины 150 символов
Таблица.Колонки.Добавить("Наименование",
    Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));

Обратите внимание на позицию квалификаторов в конструкторе ОписаниеТипов: сначала строка типов, затем КвалификаторыЧисла, КвалификаторыСтроки, КвалификаторыДаты. Поэтому для строки квалификатор идёт третьим параметром, а второй пропускается запятой.

Если тип не задать вовсе, колонка остаётся универсальной. Для разовых буферных таблиц это нормально, но для таблиц, которые пойдут в СКД или в сравнение значений, тип указывать нужно — иначе получите неожиданные результаты сортировки и группировки.

Когда тип колонки должен в точности повторять реквизит справочника или документа, не выписывайте его вручную — возьмите готовое описание из метаданных:

ТипАртикула = Метаданные.Справочники.Номенклатура.Реквизиты.Артикул.Тип;
Таблица.Колонки.Добавить("Артикул", ТипАртикула);

Колонка получит ровно тот тип и те квалификаторы длины, что заданы у реквизита, и никогда не разойдётся с ним при изменении конфигурации. Этот же приём работает со стандартными реквизитами — например, тип ссылки берётся из ...СтандартныеРеквизиты.Ссылка.Тип.

Куда встанет колонка и как поменять порядок

Добавить всегда дописывает колонку в конец. Если нужна строго определённая позиция — используйте Вставить, первый параметр здесь индекс (отсчёт с нуля):

// поставить "Номер строки" самой первой колонкой
Таблица.Колонки.Вставить(0, "НомерСтроки", Новый ОписаниеТипов("Число"), "№");

Остальные параметры Вставить те же, что у Добавить: имя, тип, заголовок, ширина.

Уже существующую колонку можно подвинуть методом Сдвинуть. Второй параметр — смещение: отрицательное влево, положительное вправо.

Колонка = Таблица.Колонки.Найти("Сумма");
Таблица.Колонки.Сдвинуть(Колонка, -1); // на одну позицию левее

Узнать текущий номер колонки помогает Индекс, а общее число — Количество:

Позиция = Таблица.Колонки.Индекс(Таблица.Колонки.Найти("Сумма"));
Всего   = Таблица.Колонки.Количество();

Метод Найти возвращает либо объект колонки, либо Неопределено, если такой нет. Это главный способ проверить существование колонки перед любыми операциями.

Лишнюю колонку убирают методом Удалить — ему передают имя, индекс или сам объект колонки:

Таблица.Колонки.Удалить("Черновик");

Данные удалённой колонки исчезают во всех строках вместе с ней, отдельно чистить строки не нужно. Если же таблицу надо пересобрать с нуля, строки убирает Таблица.Очистить(), после чего можно заново описать нужный набор колонок.

Создание колонок по условию и списком

Самый частый сценарий динамики — набор колонок заранее неизвестен: он приходит из массива имён, из запроса или зависит от периода отчёта. Шаблон один и тот же — перебираем источник и добавляем колонки в цикле, проверяя, что имя ещё не занято:

ИменаМесяцев = СтрРазделить("Январь,Февраль,Март,Апрель", ",");

Для Каждого Месяц Из ИменаМесяцев Цикл
    Если Таблица.Колонки.Найти(Месяц) = Неопределено Тогда
        Таблица.Колонки.Добавить(Месяц, Новый ОписаниеТипов("Число",
            Новый КвалификаторыЧисла(15, 2)), Месяц);
    КонецЕсли;
КонецЦикла;

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

Колонки по условию — тот же приём, только добавление под Если:

Если ВыводитьНДС Тогда
    Таблица.Колонки.Добавить("НДС", Новый ОписаниеТипов("Число",
        Новый КвалификаторыЧисла(15, 2)), "НДС");
    Таблица.Колонки.Добавить("СуммаСНДС", Новый ОписаниеТипов("Число",
        Новый КвалификаторыЧисла(15, 2)), "Всего с НДС");
КонецЕсли;

Когда имена колонок берутся из результата запроса (например, список складов или статей затрат), перебирайте выборку и так же добавляйте поля. Имя при этом лучше делать техническим (Склад_» + Код), а человекочитаемое название класть в Заголовок, чтобы не упереться в правила идентификаторов.

Чем заполнить новую колонку

Когда колонку добавляют в уже заполненную таблицу, значения проставляются автоматически по типу:

Тип колонкиЗначение в существующих строках
Число0
Строкапустая строка ""
БулевоЛожь
Датапустая дата
Ссылкапустая ссылка
Тип не заданНеопределено

Дальше значения заполняют одним из двух способов. Обход строк — когда значение считается по другим колонкам:

Для Каждого Строка Из Таблица Цикл
    Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;

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

Таблица.ЗагрузитьКолонку(МассивСумм, "Сумма");

Число элементов массива должно совпадать с числом строк таблицы, иначе часть строк останется с пустым значением, а лишние элементы просто отбросятся. Обратная операция — ВыгрузитьКолонку("Сумма") — вернёт массив всех значений колонки.

Что учесть на таблицах в тысячи строк

Само добавление колонки от размера таблицы почти не зависит — платформа инициализирует новое поле значением по умолчанию быстро даже на десятках тысяч строк. Тормозит обычно не вставка колонки, а то, что делают после неё.

Главная ловушка — поиск строк в цикле. Если после добавления колонки вы в цикле ищете строки методом Найти или НайтиСтроки, на большой таблице это даёт квадратичную нагрузку. Решение — заранее построить индекс по колонкам, по которым ищете:

Таблица.Индексы.Добавить("Номенклатура");
Таблица.Индексы.Добавить("Склад, Номенклатура");

После этого НайтиСтроки по проиндексированным колонкам работает на порядки быстрее.

Ещё пара практичных приёмов на объёмах:

  • 🧮 заполняйте расчётную колонку одним проходом Для Каждого, а не через повторный Найти по той же таблице;
  • 📦 если структура колонок известна заранее, объявляйте все колонки до загрузки строк — тогда платформа не переинициализирует значения по мере добавления полей;
  • 🧩 для переноса структуры в другую таблицу есть СкопироватьКолонки() — вернёт пустую таблицу с тем же набором колонок, строки перебирать не нужно.

Собираем таблицу под отчёт: пример целиком

Соберём живой пример — таблицу для простого отчёта по продажам. Сначала объявляем базовые колонки, затем добавляем колонки-месяцы по периоду, заполняем данные и в конце дописываем расчётную колонку «Итого». Так все приёмы из статьи работают вместе.

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

// 2. динамически — колонка под каждый месяц
ИменаМесяцев = СтрРазделить("Январь,Февраль,Март", ",");
Для Каждого Месяц Из ИменаМесяцев Цикл
    Если Продажи.Колонки.Найти(Месяц) = Неопределено Тогда
        Продажи.Колонки.Добавить(Месяц, Новый ОписаниеТипов("Число",
            Новый КвалификаторыЧисла(15, 2)), Месяц, 10);
    КонецЕсли;
КонецЦикла;

// 3. заполнили строки (здесь — учебными данными)
Строка = Продажи.Добавить();
Строка.Номенклатура = Товар;
Строка.Январь  = 1000;
Строка.Февраль = 1500;
Строка.Март    = 1200;

// 4. расчётная колонка поверх уже заполненной таблицы
Продажи.Колонки.Добавить("Итого", Новый ОписаниеТипов("Число",
    Новый КвалификаторыЧисла(15, 2)), "Итого за квартал", 12);

Для Каждого Строка Из Продажи Цикл
    Строка.Итого = Строка.Январь + Строка.Февраль + Строка.Март;
КонецЦикла;

Ключевой момент — порядок. Колонку «Итого» добавляем после загрузки строк: во всех строках она сразу получит значение 0, а цикл пересчитает его по месяцам. Если бы месяцев было двенадцать, их имена так же пришли бы списком или из запроса, а не писались руками.

Тот же подход годится для кросс-таблиц любого вида: строки — один разрез (товары, подразделения), а динамические колонки — второй (периоды, склады). Набор колонок при этом рождается из данных, а не фиксируется в коде заранее.

Ошибки, на которых спотыкаются чаще всего

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

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

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

Можно ли добавить колонку, если таблица уже заполнена строками?
Да. Новая колонка сразу появляется во всех существующих строках со значением по умолчанию для своего типа: число — 0, строка — пустая, булево — Ложь. Если тип не задан, во всех строках будет Неопределено.
Чем отличается Имя колонки от Заголовка?
Имя — программный идентификатор для обращения в коде (Строка.Сумма), подчиняется правилам имён переменных. Заголовок — текст для показа на форме и в табличном документе, в нём допустимы пробелы и знаки. Это независимые свойства.
Как проверить, есть ли уже такая колонка?
Методом Колонки.Найти(Имя). Он вернёт объект колонки, если она есть, или Неопределено, если нет. На этой проверке строят безопасное добавление в циклах.
Как переименовать колонку, не теряя данные?
Присвоить новое значение свойству Имя у объекта колонки: Таблица.Колонки.Найти("Старое").Имя = "Новое". Данные в строках сохранятся, меняется только идентификатор.
Как скопировать набор колонок в другую таблицу?
Метод Таблица.СкопироватьКолонки() вернёт новую пустую таблицу с тем же составом и типами колонок. Если нужны и колонки, и строки — используйте Таблица.Скопировать().