Менять структуру таблицы значений 1С: колонки, типы, порядок

· 9 мин

Содержание

Колонки таблицы значений меняют через коллекцию Колонки: Добавить() создаёт колонку, Удалить() убирает, свойство Имя переименовывает. Тип колонки после создания лучше не трогать: проще завести новую колонку с нужным типом, перенести в неё данные и удалить старую. Ниже все способы с рабочим кодом для платформы 8.3.

Есть два пути. Либо строите структуру один раз при создании таблицы и потом только наполняете её. Либо правите уже заполненную таблицу, которую вам передали из запроса, из параметра функции или из табличной части. Во втором случае важно не сломать данные и не получить тормоза на больших объёмах.

Как создать таблицу и задать колонки с нуля

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

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

У метода Добавить четыре параметра, обязательное только имя:

ПараметрЧто задаётЗачем
ИмяИдентификатор колонкиОбращение Строка.Имя, поиск, отбор
ТипЗначенийОграничение типаПриведение значений, подсказки в отладчике
ЗаголовокПодпись для пользователяВидна, если таблицу выводят на форму
ШиринаШирина колонкиТолько для отображения

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

Тип можно не указывать. Тогда в колонку ляжет любое значение, и это удобно для промежуточных расчётов. Но типизированная колонка сама приводит значения: если в числовую колонку записать строку "12", получите 0. Поэтому для данных, которые потом попадут в запрос или документ, тип лучше задавать.

Как добавить колонку в уже заполненную таблицу

Новая колонка добавляется в конец и сразу получает значение Неопределено в каждой существующей строке (для типизированной колонки это пустое значение типа, например 0 для числа).

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

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

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

Таблица.ЗаполнитьЗначения(Ложь, "Обработано");

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

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

Если Таблица.Колонки.Найти("Сумма") = Неопределено Тогда
    Таблица.Колонки.Добавить("Сумма");
КонецЕсли;

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

Колонку можно создать и из данных. Метод ЗагрузитьКолонку кладёт в неё значения массива по порядку строк:

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

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

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

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

Таблица.Колонки.Удалить("Комментарий");
Таблица.Колонки.Удалить(0);
Таблица.Колонки.Удалить(Таблица.Колонки.Найти("Сумма"));

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

Удаление внутри цикла по колонкам требует обратного порядка, иначе индексы поплывут:

Для Сч = 1 По Таблица.Колонки.Количество() Цикл
    Колонка = Таблица.Колонки[Таблица.Колонки.Количество() - Сч];
    Если Лев(Колонка.Имя, 4) = "Врем" Тогда
        Таблица.Колонки.Удалить(Колонка);
    КонецЕсли;
КонецЦикла;

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

Переименование — присвоение свойству Имя:

Колонка = Таблица.Колонки.Найти("Кол");
Колонка.Имя = "Количество";

Здесь колонка Кол получает имя Количество. Обращения по прежнему имени в коде после переименования перестанут работать, а ошибка проявится только в момент выполнения. Если таблица передаётся в другие процедуры, переименование ломает их молча.

Не переименовывайте колонки таблицы, которая выведена на управляемую форму как реквизит или связана с динамическим списком. Привязка идёт по имени, и поле на форме потеряет данные.

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

Как поменять тип данных колонки

Тип колонки задаётся в момент создания. Менять его у существующей колонки не рекомендуется: часть значений может не пройти приведение. Правильная последовательность такая:

  1. Создайте колонку с временным именем и нужным типом.
  2. Перенесите значения циклом или через ЗагрузитьКолонку.
  3. Удалите старую колонку.
  4. Дайте новой колонке прежнее имя.
Таблица.Колонки.Добавить("КоличествоНовое", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));

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

Таблица.Колонки.Удалить("Количество");
Таблица.Колонки.КоличествоНовое.Имя = "Количество";

Преобразование строки в число через Число() падает на значениях вроде "12 шт". Если исходные данные грязные, оберните вызов в Попытка … Исключение и пишите 0 или фиксируйте проблемную строку в журнале.

Строковые колонки задавайте с квалификатором длины. Новый КвалификаторыСтроки(50) ограничит значение 50 символами, длиннее будет обрезано. Для дат укажите состав: Новый КвалификаторыДаты(ЧастиДаты.Дата) отсекает время.

Составной тип собирают так: Новый ОписаниеТипов("СправочникСсылка.Номенклатура, СправочникСсылка.Контрагенты"). Колонка принимает ссылки обоих видов и пустое значение.

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

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

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

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

Первый параметр — какие строки копировать (пусто — все), второй — какие колонки оставить. Копия получает колонки в том порядке, в каком вы их перечислили. Заодно это способ выкинуть сразу несколько ненужных колонок, не удаляя по одной.

Второй способ: построить таблицу заново и перенести данные через ЗагрузитьКолонку и ВыгрузитьКолонку или циклом. Он длиннее, зато позволяет заодно поменять типы.

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

Как скопировать структуру и обойти колонки циклом

Бывает нужна пустая таблица с теми же колонками, например чтобы собрать в неё отфильтрованные строки. Для этого есть СкопироватьКолонки:

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

Метод переносит имена, типы, заголовки и ширину, но не данные. Процедура ЗаполнитьЗначенияСвойств копирует значения одноимённых колонок, поэтому строки не приходится заполнять по одной колонке.

Перебрать колонки и узнать их свойства можно так:

Для Каждого Колонка Из Таблица.Колонки Цикл
    Сообщить(Колонка.Имя + " / " + Колонка.Заголовок + " / " + Колонка.ТипЗначений);
КонецЦикла;

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

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

Индексы таблицы значений и поиск по колонкам

Если по таблице много раз ищут строки по одним и тем же колонкам, добавьте индекс. Таблица построит его один раз, и методы Найти и НайтиСтроки перестанут перебирать всё подряд.

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

Индекс набирается из колонок через запятую. Он работает, когда в отборе участвуют колонки, с которых он начинается. Отбор только по Склад индекс из примера не ускорит.

Что учесть:

  • 📌 Индекс строится на момент добавления и обновляется при изменении данных, поэтому массовое заполнение дешевле делать до создания индекса.
  • 📌 При удалении колонки индекс, в котором она участвовала, удаляется вместе с ней.
  • 📌 Индекс бесполезен на таблицах в десятки строк: выигрыш начинается с тысяч.

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

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

Первый параметр — колонки группировки, второй — суммируемые. Все остальные колонки исчезнут из таблицы, учитывайте это, если они ещё понадобятся.

Производительность: что тормозит при правке структуры

Добавление колонки в таблицу на 100 000 строк само по себе быстрое, а вот заполнение её циклом на клиенте может занять заметное время. Несколько правил:

  • ⚡ Выполняйте обработку на сервере. Каждое обращение клиент-сервер стоит дорого, цикл по большой таблице в клиентском коде будет медленным.
  • ⚡ Не вызывайте Колонки.Найти() внутри цикла по строкам. Один раз найдите колонку до цикла или просто обращайтесь Строка.ИмяКолонки.
  • ⚡ Если нужно создать несколько колонок с вычислениями, делайте один проход по строкам и заполняйте все сразу, а не по циклу на каждую колонку.
  • ⚡ Для сложных преобразований часто быстрее поручить дело запросу. Результат запроса выгружается в таблицу значений уже с нужными колонками и типами: РезультатЗапроса.Выгрузить(). Колонки задаются в тексте запроса псевдонимами через КАК, тип можно привести функцией ВЫРАЗИТЬ.
ЗадачаЧто быстрее
Добавить вычисляемую колонку на 100 тыс. строкОдин цикл на сервере
Оставить 3 колонки из 20Скопировать(, "…"), а не 17 вызовов Удалить
Многократный поиск по ключуИндекс или соответствие
АгрегацияСвернуть или запрос

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

Какие ошибки возникают чаще всего

Типичные сообщения при работе с колонками:

  • Поле объекта не обнаружено (Сумма) — вы обратились к колонке, которой в таблице нет. Причина: опечатка, колонку удалили или не добавили в этой ветке кода. Проверьте через Колонки.Найти.
  • Ошибка при добавлении колонки с именем, которое уже занято, — повторное добавление. Добавляйте с проверкой через Найти.
  • Индекс за границами диапазона — обращение Колонки[5] при пяти колонках; индексы начинаются с нуля.
  • Числа превратились в нули после смены типа — значения не прошли приведение. Перед копированием проверяйте ТипЗнч и пробуйте Число() внутри Попытка.
  • Пропал порядок колонок на форме — таблицу пересоздали, а форма ждала прежние имена.
Диагностика: выберите симптом

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

Можно ли переименовать колонку в готовой таблице значений?
Да, присвойте новое значение свойству Имя у найденной колонки. После этого все обращения по старому имени перестанут работать, поэтому поправьте код, который читает эту таблицу.
Как удалить сразу несколько колонок?
Проще всего скопировать таблицу с нужным списком колонок: Таблица.Скопировать(, "Колонка1, Колонка2"). Всё, что не вошло в список, в копии отсутствует.
Как узнать, есть ли колонка с таким именем?
Вызовите Таблица.Колонки.Найти("Имя"). Если результат равен Неопределено, колонки нет.
Как получить список всех колонок таблицы?
Обойдите коллекцию циклом Для Каждого Колонка Из Таблица.Колонки Цикл и читайте Колонка.Имя, Колонка.ТипЗначений, Колонка.Заголовок.
Меняется ли тип у уже записанных значений при смене типа колонки?
Менять тип существующей колонки не стоит. Создайте новую колонку нужного типа, перенесите значения с проверкой и удалите прежнюю.