Смена типа колонки Таблицы Значений: новая колонка вместо правки старой

· 9 мин

Содержание

Свойство ТипЗначения у колонки Таблицы Значений доступно только для чтения. Строка Таблица.Колонки.Цена.ТипЗначения = Новый ОписаниеТипов("Число") вызовет ошибку. Тип колонки задаётся в момент создания и дальше не меняется.

Выход один: создать колонку с нужным типом, перенести в неё данные с приведением значений, старую колонку удалить. Ниже разобрано, как это сделать без потери данных, что происходит с «неподходящими» значениями и как не нарваться на тихие ошибки.

Как устроена типизация колонок

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

Разница заметна сразу:

КолонкаЧто запишемЧто получим в ячейке
Без типастрока "15"строка "15"
Тип Числострока "15"число 15
Тип Числострока "abc"число 0
Тип Строкачисло 15строка "15"

Последние две строки — источник большинства бед. Значение, которое не удалось привести, не вызывает исключения. Оно молча превращается в пустое значение типа. Данные потерялись, а ошибки в журнале нет.

Если колонка типизирована, проверяйте результат переноса сами. Платформа не скажет, что вместо суммы в ячейке теперь ноль.

Что ещё важно знать о типах колонок:

  • 🔹 Составной тип разрешён: Новый ОписаниеТипов("Число, Строка"). Тогда допустимы оба типа.
  • 🔹 Для числа, строки и даты можно задать квалификаторы: разрядность, длину, состав даты. Они влияют на округление и обрезание.
  • 🔹 Ссылочные типы (СправочникСсылка.Номенклатура) приводятся только к ссылкам этого же вида. Строку в ссылку само присвоение не превратит.
  • 🔹 Тип Неопределено и пустое описание типов означают «любой тип». Это поведение колонки без типа.

Добавление колонки с нужным типом

Конструктор описания типов принимает имена типов строкой и квалификаторы:

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

ОписаниеСтроки = Новый ОписаниеТипов("Строка",,
    Новый КвалификаторыСтроки(100));

ОписаниеДаты = Новый ОписаниеТипов("Дата",,,
    Новый КвалификаторыДаты(ЧастиДаты.Дата));

Порядок параметров конструктора: типы, квалификаторы числа, квалификаторы строки, квалификаторы даты. Пропущенные параметры оставляйте пустыми запятыми, как в примере.

Колонку с типом добавляют методом Колонки.Добавить с тремя параметрами: имя, описание типов и заголовок:

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

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

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

Безопасный алгоритм: от старой колонки к новой

Допустим, в таблице колонка Сумма содержит строки вида "1500", а нужны числа. Порядок действий:

  1. Создайте новую колонку с временным именем и нужным типом.
  2. Пройдите по строкам и запишите в неё приведённое значение старой.
  3. Удалите старую колонку.
  4. Если важно имя, создайте колонку заново или работайте с новым именем дальше.

Вот код целиком:

ОписаниеЧисла = Новый ОписаниеТипов("Число",
    Новый КвалификаторыЧисла(15, 2));

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

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

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

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

Что нужно сделать до удаления старой колонки:

  • ✅ проверить, что все нужные значения попали в новую колонку (например, сравнить суммы по обеим);
  • ✅ убедиться, что нигде дальше по коду не используется старое имя;
  • ✅ если таблица передаётся в другой метод или на форму, проверить, не привязаны ли к старой колонке поля табличной части формы.

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

Если нужно сохранить исходное имя

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

Таблица.Колонки.Добавить("Врем", ОписаниеЧисла);
Для Каждого Строка Из Таблица Цикл
    Строка.Врем = ОписаниеЧисла.ПривестиЗначение(Строка.Сумма);
КонецЦикла;
Таблица.Колонки.Удалить("Сумма");

Таблица.Колонки.Добавить("Сумма", ОписаниеЧисла);
Для Каждого Строка Из Таблица Цикл
    Строка.Сумма = Строка.Врем;
КонецЦикла;
Таблица.Колонки.Удалить("Врем");

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

Альтернатива: пересобрать таблицу

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

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

Для Каждого Строка Из Таблица Цикл
    Нов = Результат.Добавить();
    Нов.Наименование = ОписаниеСтроки.ПривестиЗначение(Строка.Наименование);
    Нов.Сумма = ОписаниеЧисла.ПривестиЗначение(Строка.Сумма);
    Нов.Дата = ОписаниеДаты.ПривестиЗначение(Строка.Дата);
КонецЦикла;

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

Работа с колонкой без типа

Колонки без типа чаще всего получаются, когда таблица строится из результата запроса с «Выразить» без указания типа или из внешних данных, либо когда разработчик просто не задал тип. Значения в них разнородные: у одной строки число, у другой строка, у третьей пусто.

Перед приведением посмотрите, что там лежит:

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

Для Каждого Пара Из Типы Цикл
    Сообщить(Строка(Пара.Ключ));
КонецЦикла;

Так вы увидите реальный набор типов. Дальше решение по обстоятельствам:

  • 📌 если нужны только числа, а в данных есть и строки с текстом, решите заранее, что делать с текстом: игнорировать, считать нулём или вынести в отдельный список ошибок;
  • 📌 пустая строка "" при приведении к числу даст 0, при приведении к дате — пустую дату 01.01.0001;
  • 📌 Неопределено и NULL при приведении превращаются в пустое значение типа.

Если нужно не потерять «плохие» строки, приводите вручную и собирайте проблемные в отдельную таблицу:

Для Каждого Строка Из Таблица Цикл
    Приведено = ОписаниеЧисла.ПривестиЗначение(Строка.Значение);
    Если Приведено = 0 И Строка.Значение <> 0 Тогда
        // значение не число: запоминаем номер строки
        Ошибки.Добавить(Таблица.Индекс(Строка) + 1);
    КонецЕсли;
    Строка.Число = Приведено;
КонецЦикла;

Здесь Ошибки — заранее созданный список значений. Условие «приведено 0, а исходное не 0» ловит именно потерянные значения, а честные нули пропускает.

ОписаниеТипов для сложных случаев

Часть задач простым "Число" не решается. Вот что встречается на практике.

Составной тип. Колонка должна принимать и число, и строку (например, «код» бывает цифровым и буквенным):

Описание = Новый ОписаниеТипов("Число, Строка");

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

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

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

Расширение существующего описания. Допустим, у колонки уже есть тип и нужно получить расширенное описание «старое плюс ещё один тип». Берите свойство ТипЗначения существующей колонки как исходное:

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

Это не меняет старую колонку, а даёт описание для новой.

ЗадачаПодход
Строка → числоПривестиЗначение, затем проверка на потерю
Число → строкаприведение; для формата используйте Формат()
Строка → датаприведение работает не для любых форматов; сложные форматы разбирайте вручную
Строка → ссылказапрос или поиск по реквизиту, не приведение
Любой → составнойописание из нескольких типов, значение не меняется

Для числа в строку с нужным форматом (разделители, нули) приведения мало: оно даёт стандартное представление. Применяйте Формат(Число, "ЧГ=0") или СтрШаблон.

Задать тип на входе: запрос и копирование

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

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    ВЫРАЗИТЬ(Данные.Сумма КАК ЧИСЛО(15, 2)) КАК Сумма,
    |    ВЫРАЗИТЬ(Данные.Описание КАК СТРОКА(100)) КАК Описание
    |ИЗ
    |    ВТ_Данные КАК Данные";
Таблица = Запрос.Выполнить().Выгрузить();

Колонки результата получат типы из выражения ВЫРАЗИТЬ. Переносить данные потом не придётся. Для поля, которое в запросе приходит составным или нетипизированным (например, из объединения разных источников), это обычно единственное разумное место для приведения.

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

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

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

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

Типичные ошибки при смене типа

Здесь собрано то, на чём спотыкаются чаще всего, без повторения шагов выше.

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

Проверка после операции занимает пару минут и спасает от «молчаливых» ошибок:

Что проверить после смены типа
Выполнено: 0 / 5

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

Можно ли поменять тип колонки без создания новой?
Нет. ТипЗначения у существующей колонки только для чтения. Создавайте колонку с нужным типом и переносите данные.
Что будет, если записать в числовую колонку текст?
Платформа приведёт значение к типу колонки. Текст, не похожий на число, превратится в 0, и сообщения об этом не будет.
Как получить колонку с прежним именем, но другим типом?
Перенесите данные во временную колонку, удалите оригинал, создайте колонку с прежним именем и новым типом и скопируйте значения обратно.
Чем отличается колонка без типа от колонки с составным типом?
Колонка без типа принимает любое значение. Колонка с составным типом принимает только перечисленные в описании типы, остальные приводятся или обнуляются.
Нужно ли очищать таблицу перед сменой типа?
Не нужно. Наоборот, данные нужны для переноса. Очищайте только после проверки нового результата.