Менять структуру таблицы значений 1С: колонки, типы, порядок
Содержание
Колонки таблицы значений меняют через коллекцию Колонки: Добавить() создаёт колонку, Удалить() убирает, свойство Имя переименовывает. Тип колонки после создания лучше не трогать: проще завести новую колонку с нужным типом, перенести в неё данные и удалить старую. Ниже все способы с рабочим кодом для платформы 8.3.
Есть два пути. Либо строите структуру один раз при создании таблицы и потом только наполняете её. Либо правите уже заполненную таблицу, которую вам передали из запроса, из параметра функции или из табличной части. Во втором случае важно не сломать данные и не получить тормоза на больших объёмах.
Как создать таблицу и задать колонки с нуля
Таблицу создают конструктором Новый ТаблицаЗначений. После этого она пустая: ни строк, ни колонок. Колонки добавляют по одной.
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура");
Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Таблица.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка"), "Примечание", 40);
У метода Добавить четыре параметра, обязательное только имя:
| Параметр | Что задаёт | Зачем |
|---|---|---|
| Имя | Идентификатор колонки | Обращение Строка.Имя, поиск, отбор |
| ТипЗначений | Ограничение типа | Приведение значений, подсказки в отладчике |
| Заголовок | Подпись для пользователя | Видна, если таблицу выводят на форму |
| Ширина | Ширина колонки | Только для отображения |
Имя колонки подчиняется тем же правилам, что и имя переменной: без пробелов и спецсимволов, не с цифры. Для заголовка ограничений нет, там пишите как удобно читателю.
Тип можно не указывать. Тогда в колонку ляжет любое значение, и это удобно для промежуточных расчётов. Но типизированная колонка сама приводит значения: если в числовую колонку записать строку "12", получите 0. Поэтому для данных, которые потом попадут в запрос или документ, тип лучше задавать.
Как добавить колонку в уже заполненную таблицу
Новая колонка добавляется в конец и сразу получает значение Неопределено в каждой существующей строке (для типизированной колонки это пустое значение типа, например 0 для числа).
Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Для Каждого Строка Из Таблица Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
Если нужна одна и та же константа во всех строках, не гоняйте цикл вручную. Метод ЗаполнитьЗначения делает это за один вызов:
Таблица.ЗаполнитьЗначения(Ложь, "Обработано");
Первый параметр — значение, второй — список колонок через запятую. Без второго параметра заполнятся все колонки, будьте осторожны.
Перед добавлением проверьте, что такой колонки ещё нет. Повторное добавление с тем же именем вызовет исключение:
Если Таблица.Колонки.Найти("Сумма") = Неопределено Тогда
Таблица.Колонки.Добавить("Сумма");
КонецЕсли;
Найти возвращает колонку или Неопределено. Это стандартный приём для процедур, которые могут вызываться по таблице уже в готовом виде.
Колонку можно создать и из данных. Метод ЗагрузитьКолонку кладёт в неё значения массива по порядку строк:
Таблица.Колонки.Добавить("Код");
Таблица.ЗагрузитьКолонку(МассивКодов, "Код");
Длина массива должна совпадать с числом строк. Лишние элементы отбросятся, недостающие оставят пустые значения. Обратная операция, ВыгрузитьКолонку("Код"), возвращает массив значений.
Как удалить и переименовать колонку
Удаляют по имени, по индексу или по самому объекту колонки:
Таблица.Колонки.Удалить("Комментарий");
Таблица.Колонки.Удалить(0);
Таблица.Колонки.Удалить(Таблица.Колонки.Найти("Сумма"));
Данные колонки исчезают вместе с ней, вернуть их нельзя. Если значения ещё пригодятся, сначала выгрузите их в массив.
Удаление внутри цикла по колонкам требует обратного порядка, иначе индексы поплывут:
Для Сч = 1 По Таблица.Колонки.Количество() Цикл
Колонка = Таблица.Колонки[Таблица.Колонки.Количество() - Сч];
Если Лев(Колонка.Имя, 4) = "Врем" Тогда
Таблица.Колонки.Удалить(Колонка);
КонецЕсли;
КонецЦикла;
Здесь граница цикла считается один раз, до первого прохода, а элемент берётся с конца. Поэтому удаление текущей колонки не сдвигает те, что ещё не обработаны.
Переименование — присвоение свойству Имя:
Колонка = Таблица.Колонки.Найти("Кол");
Колонка.Имя = "Количество";
Здесь колонка Кол получает имя Количество. Обращения по прежнему имени в коде после переименования перестанут работать, а ошибка проявится только в момент выполнения. Если таблица передаётся в другие процедуры, переименование ломает их молча.
Не переименовывайте колонки таблицы, которая выведена на управляемую форму как реквизит или связана с динамическим списком. Привязка идёт по имени, и поле на форме потеряет данные.
Надёжный вариант для передаваемых таблиц: не переименовывать, а добавить колонку с новым именем, скопировать значения и удалить старую. Тогда место сбоя видно сразу.
Как поменять тип данных колонки
Тип колонки задаётся в момент создания. Менять его у существующей колонки не рекомендуется: часть значений может не пройти приведение. Правильная последовательность такая:
- Создайте колонку с временным именем и нужным типом.
- Перенесите значения циклом или через
ЗагрузитьКолонку. - Удалите старую колонку.
- Дайте новой колонке прежнее имя.
Таблица.Колонки.Добавить("КоличествоНовое", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Для Каждого Строка Из Таблица Цикл
Строка.КоличествоНовое = ?(ТипЗнч(Строка.Количество) = Тип("Строка"),
Число(Строка.Количество), Строка.Количество);
КонецЦикла;
Таблица.Колонки.Удалить("Количество");
Таблица.Колонки.КоличествоНовое.Имя = "Количество";
Преобразование строки в число через Число() падает на значениях вроде "12 шт". Если исходные данные грязные, оберните вызов в Попытка … Исключение и пишите 0 или фиксируйте проблемную строку в журнале.
Строковые колонки задавайте с квалификатором длины. Новый КвалификаторыСтроки(50) ограничит значение 50 символами, длиннее будет обрезано. Для дат укажите состав: Новый КвалификаторыДаты(ЧастиДаты.Дата) отсекает время.
Составной тип собирают так: Новый ОписаниеТипов("СправочникСсылка.Номенклатура, СправочникСсылка.Контрагенты"). Колонка принимает ссылки обоих видов и пустое значение.
Как переставить колонки и изменить порядок
Порядок колонок важен там, где таблицу выводят пользователю или выгружают в файл. В самой таблице порядок определяется порядком добавления. Есть два рабочих способа.
Первый способ: создать копию с нужным списком колонок.
НоваяТаблица = Таблица.Скопировать(, "Номенклатура, Цена, Количество, Сумма");
Первый параметр — какие строки копировать (пусто — все), второй — какие колонки оставить. Копия получает колонки в том порядке, в каком вы их перечислили. Заодно это способ выкинуть сразу несколько ненужных колонок, не удаляя по одной.
Второй способ: построить таблицу заново и перенести данные через ЗагрузитьКолонку и ВыгрузитьКолонку или циклом. Он длиннее, зато позволяет заодно поменять типы.
Для вывода на форму порядок обычно задают не в коде таблицы, а в настройках поля формы. Если колонки нужно показать в другом порядке только пользователю, меняйте порядок элементов формы и не трогайте саму таблицу.
Как скопировать структуру и обойти колонки циклом
Бывает нужна пустая таблица с теми же колонками, например чтобы собрать в неё отфильтрованные строки. Для этого есть СкопироватьКолонки:
Результат = Таблица.СкопироватьКолонки();
Для Каждого Строка Из Таблица Цикл
Если Строка.Количество > 100 Тогда
НоваяСтрока = Результат.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
КонецЕсли;
КонецЦикла;
Метод переносит имена, типы, заголовки и ширину, но не данные. Процедура ЗаполнитьЗначенияСвойств копирует значения одноимённых колонок, поэтому строки не приходится заполнять по одной колонке.
Перебрать колонки и узнать их свойства можно так:
Для Каждого Колонка Из Таблица.Колонки Цикл
Сообщить(Колонка.Имя + " / " + Колонка.Заголовок + " / " + Колонка.ТипЗначений);
КонецЦикла;
Это удобно в универсальных обработках, которые не знают структуру заранее: выгрузка таблицы в файл, вывод в табличный документ, сравнение двух таблиц. Если нужно сравнить структуры, берите имена из Колонки обеих таблиц и проверяйте наличие каждой через Найти.
Ещё один частый приём: получить таблицу с нужной структурой из метаданных. Для табличной части документа это Документ.Товары.Выгрузить(), для реквизита формы типа ТаблицаЗначений — РеквизитФормыВЗначение("Объект") или РеквизитФормыВЗначение("ИмяРеквизита"). Полученный объект уже является обычной таблицей значений, и все приёмы из этой статьи к нему применимы. Изменение структуры такой копии не затрагивает табличную часть: чтобы вернуть данные в документ, их нужно загрузить обратно через Загрузить(), а набор колонок при этом должен совпадать с составом реквизитов табличной части.
Индексы таблицы значений и поиск по колонкам
Если по таблице много раз ищут строки по одним и тем же колонкам, добавьте индекс. Таблица построит его один раз, и методы Найти и НайтиСтроки перестанут перебирать всё подряд.
Таблица.Индексы.Добавить("Номенклатура, Склад");
Индекс набирается из колонок через запятую. Он работает, когда в отборе участвуют колонки, с которых он начинается. Отбор только по Склад индекс из примера не ускорит.
Что учесть:
- 📌 Индекс строится на момент добавления и обновляется при изменении данных, поэтому массовое заполнение дешевле делать до создания индекса.
- 📌 При удалении колонки индекс, в котором она участвовала, удаляется вместе с ней.
- 📌 Индекс бесполезен на таблицах в десятки строк: выигрыш начинается с тысяч.
Для группировки и подсчёта итогов индекс не нужен. Там используйте Свернуть, он сам объединяет строки по указанным колонкам и суммирует числовые:
Таблица.Свернуть("Номенклатура", "Количество, Сумма");
Первый параметр — колонки группировки, второй — суммируемые. Все остальные колонки исчезнут из таблицы, учитывайте это, если они ещё понадобятся.
Производительность: что тормозит при правке структуры
Добавление колонки в таблицу на 100 000 строк само по себе быстрое, а вот заполнение её циклом на клиенте может занять заметное время. Несколько правил:
- ⚡ Выполняйте обработку на сервере. Каждое обращение клиент-сервер стоит дорого, цикл по большой таблице в клиентском коде будет медленным.
- ⚡ Не вызывайте
Колонки.Найти()внутри цикла по строкам. Один раз найдите колонку до цикла или просто обращайтесьСтрока.ИмяКолонки. - ⚡ Если нужно создать несколько колонок с вычислениями, делайте один проход по строкам и заполняйте все сразу, а не по циклу на каждую колонку.
- ⚡ Для сложных преобразований часто быстрее поручить дело запросу. Результат запроса выгружается в таблицу значений уже с нужными колонками и типами:
РезультатЗапроса.Выгрузить(). Колонки задаются в тексте запроса псевдонимами черезКАК, тип можно привести функциейВЫРАЗИТЬ.
| Задача | Что быстрее |
|---|---|
| Добавить вычисляемую колонку на 100 тыс. строк | Один цикл на сервере |
| Оставить 3 колонки из 20 | Скопировать(, "…"), а не 17 вызовов Удалить |
| Многократный поиск по ключу | Индекс или соответствие |
| Агрегация | Свернуть или запрос |
Если структура меняется внутри цикла по строкам той же таблицы, результат непредсказуем. Сначала приведите структуру в порядок, потом обходите данные.
Какие ошибки возникают чаще всего
Типичные сообщения при работе с колонками:
Поле объекта не обнаружено (Сумма)— вы обратились к колонке, которой в таблице нет. Причина: опечатка, колонку удалили или не добавили в этой ветке кода. Проверьте черезКолонки.Найти.- Ошибка при добавлении колонки с именем, которое уже занято, — повторное добавление. Добавляйте с проверкой через
Найти. Индекс за границами диапазона— обращениеКолонки[5]при пяти колонках; индексы начинаются с нуля.- Числа превратились в нули после смены типа — значения не прошли приведение. Перед копированием проверяйте
ТипЗнчи пробуйтеЧисло()внутриПопытка. - Пропал порядок колонок на форме — таблицу пересоздали, а форма ждала прежние имена.
Колонки нужной таблицы. Частая причина: таблица пришла из запроса, где у поля другой псевдоним.Число().Частые вопросы
Можно ли переименовать колонку в готовой таблице значений?
Имя у найденной колонки. После этого все обращения по старому имени перестанут работать, поэтому поправьте код, который читает эту таблицу.Как удалить сразу несколько колонок?
Таблица.Скопировать(, "Колонка1, Колонка2"). Всё, что не вошло в список, в копии отсутствует.Как узнать, есть ли колонка с таким именем?
Таблица.Колонки.Найти("Имя"). Если результат равен Неопределено, колонки нет.Как получить список всех колонок таблицы?
Для Каждого Колонка Из Таблица.Колонки Цикл и читайте Колонка.Имя, Колонка.ТипЗначений, Колонка.Заголовок.