ТаблицаЗначений в коде 1С: колонки, типы, заполнение и передача на клиент
Содержание
Таблица значений в 1С создаётся конструктором Новый ТаблицаЗначений, после чего колонки добавляют методом Колонки.Добавить("Имя", ОписаниеТипов), а строки — методом Добавить(). Создавать её можно только на сервере, в толстом клиенте или во внешнем соединении: в тонком и веб-клиенте такого типа нет.
Дальше всё упирается в детали. Колонка без типа ломает запрос к таблице, поиск без индекса тормозит на 100 тысячах строк, а попытка вернуть таблицу на клиент даёт ошибку. Ниже код, который можно сразу вставлять в общий модуль или модуль формы, и объяснение, где чаще всего ошибаются.
Создание таблицы значений и объявление колонок
Минимальный рабочий пример: объект, три колонки, одна строка.
&НаСервере
Процедура СоздатьТаблицуНаСервере()
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Количество = 2;
НоваяСтрока.Сумма = 1500;
КонецПроцедуры
Метод Колонки.Добавить принимает четыре параметра:
| Параметр | Что задаёт | Обязателен |
|---|---|---|
| Имя | Идентификатор колонки, по нему обращаются Строка.Сумма | Да |
| ТипЗначения | Объект ОписаниеТипов | Нет, но лучше указывать |
| Заголовок | Текст в шапке, если таблицу выводят пользователю | Нет |
| Ширина | Ширина колонки в символах | Нет |
Имя колонки подчиняется тем же правилам, что и имя переменной: буквы, цифры и подчёркивание, первым символом — не цифра. Пробелы, точки и дефисы недопустимы. Если имя приходит извне (например, из заголовков Excel), очистите его перед добавлением.
💡 Метод Добавить возвращает объект КолонкаТаблицыЗначений. Его удобно сохранить в переменную, если колонке потом нужно поменять заголовок или найти её индекс.
Как проверить, есть ли колонка
Если ТЗ.Колонки.Найти("Склад") = Неопределено Тогда
ТЗ.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
КонецЕсли;
Повторное добавление колонки с тем же именем вызывает исключение, поэтому в универсальном коде проверка обязательна. Удалить колонку можно через ТЗ.Колонки.Удалить("Склад").
Копия структуры без данных
Если нужна пустая таблица с теми же колонками, не перечисляйте их заново:
ПустаяТЗ = ТЗ.СкопироватьКолонки();
Можно передать строку с именами нужных колонок: ТЗ.СкопироватьКолонки("Номенклатура, Сумма").
Типы колонок: ОписаниеТипов и квалификаторы
Колонка без типа принимает что угодно. В памяти это работает, но ломается в трёх местах: при передаче таблицы в запрос, при выводе в табличное поле с редактированием и при подсчёте итогов по смешанным значениям.
Основные варианты описания типов:
// Число 15 знаков, 2 после запятой
ТипСумма = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
// Строка длиной 100 символов
ТипСтрока = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100));
// Дата без времени
ТипДата = Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата));
// Булево
ТипФлаг = Новый ОписаниеТипов("Булево");
// Составной тип: ссылка на контрагента или физлицо
ТипКонтрагент = Новый ОписаниеТипов("СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица");
Порядок параметров конструктора фиксирован: типы, квалификаторы числа, строки, даты. Поэтому для строки ставят одну пустую запятую, для даты — две.
Что даёт типизация на практике
- 🔢 Приведение значений. В колонку
Число(15, 2)значение 10.456 запишется как 10.46, а строка «abc» — как 0. - 🧾 Корректный запрос. Типизированную таблицу можно передать параметром и выбрать из неё во временную таблицу.
- ⚡ Предсказуемая сортировка. В колонке смешанного типа значения сортируются сначала по типу, потом по значению, и результат выглядит случайным.
Строка неограниченной длины (квалификатор 0 или без квалификатора) в запросе не участвует в группировке, сравнении и соединениях. Для колонок, которые пойдут в запрос, задавайте конкретную длину.
Создание ОписанияТипов вне цикла
Если колонки добавляются в цикле по метаданным или настройкам, создайте объекты ОписаниеТипов один раз заранее. Конструктор разбирает строку с именами типов при каждом вызове, и на сотнях колонок это заметно.
Способы заполнения таблицы данными
Таблицу можно наполнить вручную, из запроса, из другой коллекции или колонкой целиком. Каждый способ хорош для своего случая.
Построчно через Добавить и ЗаполнитьЗначенияСвойств
Для Каждого СтрокаТовара Из Документ.Товары Цикл
НоваяСтрока = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТовара);
КонецЦикла;
ЗаполнитьЗначенияСвойств копирует значения всех одноимённых свойств. Третьим параметром передают список нужных колонок, четвёртым — список исключаемых.
Для вставки строки в конкретное место используют ТЗ.Вставить(Индекс): индекс 0 даёт новую первую строку.
Из результата запроса
Самый быстрый и частый способ: платформа сама создаёт колонки с типами из полей запроса.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки";
ТЗ = Запрос.Выполнить().Выгрузить();
Имена колонок совпадают с псевдонимами полей (КАК Количество). Если данных много, а нужна только часть, отберите их условием в запросе, а не фильтром по готовой таблице.
Колонкой целиком
МассивКодов = Новый Массив;
МассивКодов.Добавить("00001");
МассивКодов.Добавить("00002");
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(11)));
Для Сч = 1 По МассивКодов.Количество() Цикл
ТЗ.Добавить();
КонецЦикла;
ТЗ.ЗагрузитьКолонку(МассивКодов, "Код");
ЗагрузитьКолонку заполняет уже существующие строки по порядку, поэтому сначала строки добавляют. Обратный метод ВыгрузитьКолонку("Код") возвращает массив значений — удобно для параметра запроса В (&Коды).
Одно значение во все строки
ТЗ.ЗаполнитьЗначения(ТекущаяДата(), "ДатаОбработки");
Без второго параметра значение запишется во все колонки, что почти никогда не нужно.
Перебор строк и чтение значений
Обходить таблицу удобнее всего циклом Для Каждого. К значению колонки обращаются через точку или по имени в квадратных скобках — второй вариант нужен, когда имя колонки хранится в переменной.
Для Каждого Строка Из ТЗ Цикл
Сообщить(СтрШаблон("%1: %2 шт.", Строка.Номенклатура, Формат(Строка.Количество, "ЧГ=0")));
ЗначениеКолонки = Строка[ИмяКолонки];
КонецЦикла;
Если нужны сразу все имена колонок, переберите коллекцию ТЗ.Колонки и читайте свойство Имя у каждой.
Работа со строками: сортировка, отбор, поиск и свёртка
Готовую таблицу редко используют как есть. Обычно её упорядочивают, ищут нужные строки или сворачивают по группировкам.
| Задача | Метод | Результат |
|---|---|---|
| Упорядочить | Сортировать("Сумма Убыв, Наименование") | Меняет исходную таблицу |
| Найти первую строку | Найти(Значение, "Колонка") | Строка или Неопределено |
| Найти все строки по условию | НайтиСтроки(Структура) | Массив строк |
| Отфильтрованная копия | Скопировать(Структура, "Колонки") | Новая таблица |
| Сгруппировать и просуммировать | Свернуть("Группировки", "Суммирования") | Меняет исходную таблицу |
| Итог по колонке | Итог("Сумма") | Число |
Поиск по нескольким колонкам
Отбор = Новый Структура("Склад, Номенклатура", Склад, Номенклатура);
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
Строка.Количество = 0;
КонецЦикла;
НайтиСтроки возвращает ссылки на строки исходной таблицы. Изменения в найденных строках сразу видны в ТЗ.
Отбор работает только на равенство. Условия «больше», «меньше», «содержит» решаются циклом с Если или запросом к таблице.
Свёртка
ТЗ.Свернуть("Номенклатура, Склад", "Количество, Сумма");
После свёртки в таблице остаются только перечисленные колонки. Одна колонка не может стоять одновременно в группировках и в суммировании. Если нужно сохранить исходник, сначала сделайте ТЗ.Скопировать().
Удаление строк по условию
Удалять строки в прямом цикле Для Каждого нельзя: после удаления индексы сдвигаются, и строка за удалённой пропускается. Правильный вариант — обратный обход:
ВсегоСтрок = ТЗ.Количество();
Для Сч = 1 По ВсегоСтрок Цикл
Строка = ТЗ[ВсегоСтрок - Сч];
Если Строка.Количество = 0 Тогда
ТЗ.Удалить(Строка);
КонецЕсли;
КонецЦикла;
Количество строк здесь зафиксировано до цикла. Индекс ВсегоСтрок - Сч при удалении задевает только строки, которые цикл уже прошёл, поэтому ничего не пропускается.
Таблица значений на больших объёмах
На сотне строк скорость не важна. На 50–500 тысячах неудачный приём превращает обработку в многоминутную.
- 🔍 Индексы для повторного поиска. Если
НайтиСтрокивызывается в цикле по одним и тем же колонкам, добавьте индекс:ТЗ.Индексы.Добавить("Номенклатура, Склад"). Отбор по этим колонкам перестаёт перебирать таблицу целиком. - 🗄 Запрос вместо вложенных циклов. Сопоставить две таблицы по ключу быстрее через временные таблицы и
ЛЕВОЕ СОЕДИНЕНИЕ, чем вложеннымДля КаждогосНайти. - 📦 Только нужные колонки.
Скопировать(, "Номенклатура, Сумма")и выборка в запросе только нужных полей уменьшают расход памяти. - 🧮 Итоги без цикла.
Итог("Сумма")иСвернутьработают внутри платформы и быстрее ручного суммирования.
Индекс замедляет добавление строк: платформа перестраивает его при каждой вставке. Добавляйте индекс после заполнения таблицы, а не перед ним.
Таблица значений как параметр запроса
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТаблицаТоваров", ТЗ);
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| &ТаблицаТоваров КАК Т
|;
|ВЫБРАТЬ
| ВТ.Номенклатура,
| ВТ.Количество,
| ЕСТЬNULL(Цены.Цена, 0) КАК Цена
|ИЗ
| ВТ_Товары КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
| ПО ВТ.Номенклатура = Цены.Номенклатура";
Результат = Запрос.Выполнить().Выгрузить();
Из параметра-таблицы можно только выбрать данные во временную таблицу, соединять её напрямую с другими источниками нельзя. Поэтому первый пакет всегда ПОМЕСТИТЬ. Имя регистра цен в примере условное — подставьте свой регистр.
Все колонки таблицы, которые выбираются в запросе, должны иметь тип. Иначе запрос упадёт на первом же пакете.
Передача таблицы между клиентом и сервером
Тип ТаблицаЗначений существует только на сервере, в толстом клиенте и во внешнем соединении. В управляемой форме на тонком клиенте с ней напрямую не поработать. Варианты:
| Способ | Когда подходит |
|---|---|
Реквизит формы типа ТаблицаЗначений | Таблицу нужно показать или редактировать на форме |
| Временное хранилище | Таблицу нужно сохранить между серверными вызовами, не отображая |
| Массив структур | Небольшая таблица, нужен разбор на клиенте |
Реквизит формы
На форме реквизит хранится как ДанныеФормыКоллекция. Чтобы получить настоящую таблицу значений, вызовите на сервере РеквизитФормыВЗначение:
&НаСервере
Процедура ОбработатьТаблицуНаСервере()
ТЗ = РеквизитФормыВЗначение("ТаблицаДанных");
ТЗ.Сортировать("Сумма Убыв");
ТЗ.Свернуть("Номенклатура", "Сумма");
ЗначениеВРеквизитФормы(ТЗ, "ТаблицаДанных");
КонецПроцедуры
Колонки реквизита описывают в редакторе формы. Если таблица значений содержит колонки, которых нет у реквизита, при записи обратно они просто не перенесутся.
💡 Для свойств объекта есть пара ДанныеФормыВЗначение и ЗначениеВДанныеФормы — они работают с произвольной коллекцией данных формы, а не с реквизитом по имени.
Временное хранилище
&НаСервере
Функция ПодготовитьДанныеНаСервере()
ТЗ = Запрос.Выполнить().Выгрузить();
Возврат ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);
КонецФункции
&НаСервере
Процедура ИспользоватьДанныеНаСервере(Адрес)
ТЗ = ПолучитьИзВременногоХранилища(Адрес);
// работа с таблицей
КонецПроцедуры
На клиенте хранится только строка-адрес. Привязка к УникальныйИдентификатор формы означает, что данные удалятся при закрытии формы.
Ошибки при работе с таблицей значений
Большинство проблем дают одни и те же тексты ошибок. Найдите свою: