К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как поменять тип значения колонки таблицы значений в 1С

Как изменить тип значения колонки таблицы значений в 1С: через ОписаниеТипов, при добавлении колонки и при пересоздании таблицы. Примеры кода и типичные ошибки.

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

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

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

Как устроен тип колонки и почему его нельзя изменить напрямую

Каждая колонка таблицы значений описывается объектом КолонкаТаблицыЗначений, у которого есть свойства Имя, ТипЗначения, Заголовок и Ширина. Свойство ТипЗначения содержит объект ОписаниеТипов — он формируется в момент добавления колонки и дальше не меняется.

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

Попытка написать Таблица.Колонки.Сумма.ТипЗначения = Новый ОписаниеТипов("Число") завершится ошибкой «Свойство доступно только для чтения». Это архитектурное решение платформы: тип влияет на внутреннее хранение уже заполненных строк, и безопасно «переключить» его на лету нельзя.

Способ 1. Задать правильный тип при добавлении колонки

Самый чистый вариант — определить тип до того, как таблица наполнена данными. Тип передаётся вторым параметром метода Колонки.Добавить():

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

// Строка неограниченной длины

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

// Строка фиксированной длины 50 символов

КвалификаторыСтроки = Новый КвалификаторыСтроки(50);

Таблица.Колонки.Добавить("Наименование",

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

// Число: 15 знаков всего, 2 после запятой

КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2);

Таблица.Колонки.Добавить("Сумма",

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

// Дата без времени

Таблица.Колонки.Добавить("ДатаДокумента",

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

// Ссылочный тип

Таблица.Колонки.Добавить("Контрагент",

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

// Составной тип: число или строка

МассивТипов = Новый Массив;

МассивТипов.Добавить(Тип("Число"));

МассивТипов.Добавить(Тип("Строка"));

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

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

Также тип можно взять из метаданных, чтобы не дублировать квалификаторы вручную:

ОписаниеТипаИзМетаданных = Метаданные.Справочники.Номенклатура

.Реквизиты.Артикул.Тип;

Таблица.Колонки.Добавить("Артикул", ОписаниеТипаИзМетаданных);

Способ 2. Пересоздать колонку с новым типом и перенести данные

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

Порядок действий
  1. 1
    Сохраните позицию колонки: запомните индекс через Таблица.Колонки.Индекс(Таблица.Колонки.Найти(ИмяКолонки))
  2. 2
    Скопируйте значения колонки в массив или временную таблицу
  3. 3
    Удалите старую колонку методом Колонки.Удалить()
  4. 4
    Добавьте колонку с тем же именем и новым ОписаниеТипов
  5. 5
    Сдвиньте колонку на исходную позицию методом Колонки.Сдвинуть()
  6. 6
    Заполните значения обратно с преобразованием типа
  7. 7
    Проверьте результат на контрольных строках
// Меняем тип колонки "Код" со Строки на Число

ИмяКолонки = "Код";

СтарыйИндекс = Таблица.Колонки.Индекс(Таблица.Колонки.Найти(ИмяКолонки));

// Сохраняем значения

СохраненныеЗначения = Новый Массив;

Для Каждого СтрокаТЗ Из Таблица Цикл

СохраненныеЗначения.Добавить(СтрокаТЗ[ИмяКолонки]);

КонецЦикла;

// Пересоздаём колонку

Таблица.Колонки.Удалить(ИмяКолонки);

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

// Возвращаем на исходную позицию

Таблица.Колонки.Сдвинуть(ИмяКолонки,

СтарыйИндекс - Таблица.Колонки.Индекс(Таблица.Колонки.Найти(ИмяКолонки)));

// Возвращаем данные

Для Инд = 0 По Таблица.Количество() - 1 Цикл

Таблица[Инд][ИмяКолонки] = СохраненныеЗначения[Инд];

КонецЦикла;

При записи значения обратно платформа попытается привести его к новому типу. Строка «123» в числовую колонку преобразуется в число 123, а вот строка «12А» станет значением по умолчанию (0 для числа). Поэтому при несовместимых типах преобразование лучше выполнять явно — об этом ниже.

Способ 3. Пересоздать всю таблицу через новую структуру колонок

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

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

НоваяТаблица.Колонки.Добавить("Номенклатура",

Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));

НоваяТаблица.Колонки.Добавить("Количество",

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

Для Каждого СтрокаТЗ Из СтараяТаблица Цикл

НоваяСтрока = НоваяТаблица.Добавить();

НоваяСтрока.Номенклатура = СтрокаТЗ.Товар; // сменилось и имя

НоваяСтрока.Количество = СтрокаТЗ.Количество;

КонецЦикла;

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

Запрос = Новый Запрос;

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

Запрос.Текст =

"ВЫБРАТЬ Т.Код, Т.Сумма

|ПОМЕСТИТЬ ВТ ИЗ &Таблица КАК Т";

Запрос.УстановитьПараметр("Таблица", СтараяТаблица);

Запрос.Выполнить();

Запрос.Текст =

"ВЫБРАТЬ

| ВЫРАЗИТЬ(ВТ.Код КАК СТРОКА(20)) КАК Код,

| ВТ.Сумма

|ИЗ ВТ КАК ВТ";

НоваяТаблица = Запрос.Выполнить().Выгрузить();

У этого способа есть ограничение: в запрос можно передать таблицу, только если все её колонки имеют типы, допустимые в запросах (примитивные типы и ссылки). Колонки составного типа «всё подряд» вызовут ошибку.

Преобразование значений при смене типа

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

  • Строка → Число. Используйте Число() с проверкой: предварительно уберите пробелы, замените запятую на точку, отфильтруйте нечисловые значения. Функция СтроковыеФункцииКлиентСервер.СтрокаВЧисло() из БСП возвращает Неопределено для некорректных строк.
  • Строка → Дата. Функция Дата() принимает строку формата «ГГГГММДДччммсс». Другие форматы разбирайте вручную или через СтроковыеФункцииКлиентСервер.СтрокаВДату().
  • Строка → Ссылка. Прямого приведения нет: ищите элемент по коду/наименованию через запрос или получайте ссылку из уникального идентификатора методом ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаУИД)).
  • Число/Дата → Строка. Обычно достаточно присвоения, но для контроля формата используйте Формат().
  • Булево. Строки «Истина»/«Ложь» и «Да»/«Нет» приводите явным сравнением — автоматическое преобразование может дать неожиданный результат.

Типичные ошибки и ограничения

Типичные ошибки
01
Попытка присвоить новое значение свойству ТипЗначения — оно доступно только для чтения, тип меняется только пересозданием колонки
02
Удаление колонки без сохранения данных — значения теряются безвозвратно
03
После пересоздания колонка оказывается в конце таблицы — позицию нужно восстанавливать методом Колонки.Сдвинуть()
04
Строка неограниченной длины в колонке, участвующей в Свернуть() или индексе, — платформа выдаст ошибку; задавайте конечную длину
05
Потеря дробной части при смене типа Число(15,2) на Число(10,0) — значения округляются молча
06
Перенос строк с ссылочными значениями в строковую колонку с надеждой потом вернуть ссылки — обратное преобразование невозможно без поиска в базе

Ещё одно ограничение касается производительности. Построчный перенос миллионов строк с преобразованием каждого значения — тяжёлая операция. Для больших таблиц выгоднее вариант с запросом и ВЫРАЗИТЬ(): он выполняется на уровне СУБД и работает заметно быстрее цикла встроенного языка.

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

Как проверить результат

После смены типа убедитесь, что колонка имеет ожидаемый тип и данные не потерялись:

// Проверка типа колонки

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

Если Не Описание.СодержитТип(Тип("Число")) Тогда

ВызватьИсключение "Тип колонки не изменился";

КонецЕсли;

// Контроль значений

Для Каждого СтрокаТЗ Из Таблица Цикл

Если ТипЗнч(СтрокаТЗ.Сумма) <> Тип("Число") Тогда

// обработка аномалии

КонецЕсли;

КонецЦикла;

Контрольные точки

Как проверить результат

Тип колонки
Норма

ТипЗначения содержит нужный тип, СодержитТип() возвращает Истина

Тревожный признак

Тип остался прежним или стал составным без необходимости

Значения строк
Норма

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

Тревожный признак

В колонке нули, пустые строки или значения по умолчанию там, где были данные

Позиция колонки
Норма

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

Тревожный признак

Колонка переехала в конец таблицы

Дробная часть и длина строк
Норма

Числа не округлены сверх нужной точности, строки не обрезаны

Тревожный признак

Потеряны знаки после запятой или хвосты длинных строк

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

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

Можно ли изменить тип колонки без удаления и потери данных?

Нет, платформа не предоставляет метода смены типа «на месте»: свойство ТипЗначения доступно только для чтения. Любой способ предполагает создание новой колонки или новой таблицы с последующим переносом значений. Данные сохраняют во временный массив или таблицу до удаления старой колонки.

Как сделать колонку, принимающую несколько типов?

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

Почему после Свернуть() пропадают строки или возникает ошибка?

Метод Свернуть() не работает с колонками типа Строка неограниченной длины в качестве группировочных. Задайте колонке конечную длину через КвалификаторыСтроки при создании или пересоздайте колонку с ограниченной длиной перед свёрткой.

Как изменить тип колонки табличной части документа, а не таблицы значений?

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

Что быстрее для большой таблицы: цикл с переносом или запрос с ВЫРАЗИТЬ()?

Для больших объёмов быстрее запрос: таблица передаётся параметром во временную таблицу, а преобразование ВЫРАЗИТЬ(... КАК ...) выполняется на уровне СУБД. Цикл встроенного языка прощe в отладке, но на сотнях тысяч строк заметно медленнее.

Почему строка «12А» после смены типа на Число стала нулём?

При присвоении значения колонке платформа пытается привести его к типу колонки. Неприводимые строки превращаются в значение по умолчанию — для числа это 0, причём без сообщения об ошибке. Перед переносом проверяйте значения функциями приведения с контролем результата, например СтрокаВЧисло() из БСП.

Итог

Что важно запомнить

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