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

· 9 мин

Содержание

Таблица значений в 1С создаётся одной строкой: ТЗ = Новый ТаблицаЗначений;. Дальше вы описываете колонки через ТЗ.Колонки.Добавить(), заполняете строки методом ТЗ.Добавить() и обрабатываете данные в памяти: сортируете, ищете, сворачиваете, выгружаете в массив или передаёте в запрос.

Объект доступен только на сервере и в толстом клиенте. В управляемой форме его нельзя держать «как есть» — там нужен реквизит формы и преобразование. Ниже — код для платформы 8.3, который работает в любой типовой конфигурации, потому что это механизм платформы, а не БП или ЗУП.

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

Пустая таблица без колонок бесполезна, поэтому сразу после Новый добавляют колонки. Метод Колонки.Добавить() принимает четыре параметра:

ПараметрЧто задаётОбязателен
ИмяИмя колонки, по нему обращаются в кодеда
ТипЗначенияОбъект ОписаниеТиповнет
ЗаголовокПодпись колонки при выводенет
ШиринаШирина в символахнет

Пример: таблица остатков товаров с типизированными колонками.

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

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

Зачем указывать тип колонки

Колонку можно создать и без типа: ТЗ.Колонки.Добавить("Сумма"). Она примет любое значение. Но у такой колонки три неудобства:

  • 🔸 в новой строке значение будет Неопределено, а не 0, и Сумма + 100 вызовет ошибку;
  • 🔸 таблицу нельзя без проблем передать параметром в запрос — платформа не знает типов полей;
  • 🔸 Итог() и Свернуть() по колонке со смешанными типами дают непредсказуемый результат.

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

Таблица из готовых данных

Чаще всего таблица значений получается не руками, а из других объектов:

  • 📦 из запроса — Запрос.Выполнить().Выгрузить();
  • 📦 из табличной части документа — Документ.Товары.Выгрузить();
  • 📦 из набора записей регистра — НаборЗаписей.Выгрузить();
  • 📦 копией структуры другой таблицы — НоваяТЗ = ТЗ.СкопироватьКолонки();.

СкопироватьКолонки() возвращает пустую таблицу с теми же колонками и типами. Удобно, когда нужно собрать отобранные строки в отдельную коллекцию.

Добавление строк и заполнение таблицы значений

Строка добавляется в конец методом Добавить(), который возвращает саму строку. Значения пишут через точку или через квадратные скобки:

НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = Товар;
НоваяСтрока.Количество = 12;
НоваяСтрока["Сумма"] = 3600;

Доступ через скобки нужен, когда имя колонки хранится в переменной: НоваяСтрока[ИмяКолонки] = Значение;.

Вставка, удаление, очистка

МетодЧто делает
Вставить(Индекс)Вставляет пустую строку на позицию; нумерация с 0
Удалить(Строка)Удаляет строку; можно передать саму строку или её индекс
Очистить()Удаляет все строки, колонки остаются
Сдвинуть(Строка, Смещение)Перемещает строку вверх (минус) или вниз (плюс)
Количество()Число строк
Индекс(Строка)Позиция строки в таблице
Получить(Индекс)Строка по индексу, то же, что ТЗ[Индекс]

Заполнение из структуры или другой строки

Если источник — структура, выборка запроса или строка другой таблицы с теми же именами полей, не переписывайте каждое поле вручную:

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

ЗаполнитьЗначенияСвойств переносит значения всех совпадающих по имени свойств. Третьим параметром можно перечислить только нужные поля, четвёртым — исключить лишние.

Чтобы одним махом записать одно значение во всю колонку, есть ЗаполнитьЗначения():

ТЗ.ЗаполнитьЗначения(Склад, "Склад");
ТЗ.ЗаполнитьЗначения(0, "Количество, Сумма");

А ЗагрузитьКолонку(Массив, "Количество") заливает в колонку значения из массива по порядку.

Сортировка, свёртка и отбор строк в памяти

Сортировать

Метод принимает строку с именами колонок и направлением: Возр (по умолчанию) или Убыв.

ТЗ.Сортировать("Склад, Сумма Убыв");

Таблица сортируется по складу, а внутри склада — от большей суммы к меньшей. Сортировка меняет саму таблицу, новую не возвращает.

Колонки со ссылками без второго параметра сортируются не по наименованию, которое видит пользователь. Чтобы получить алфавитный порядок, передайте объект сравнения: ТЗ.Сортировать("Номенклатура", Новый СравнениеЗначений);.

Свернуть и Итог

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

// До: 3 строки по одному товару на разных складах
ТЗ.Свернуть("Номенклатура", "Количество, Сумма");
// После: 1 строка на товар, колонки Склад и Комментарий исчезли

Пример с числами. Товар «Бумага А4» лежит на трёх складах: 10, 25 и 5 пачек на 3 000, 7 500 и 1 500 ₽. После свёртки по номенклатуре останется одна строка: 40 пачек на 12 000 ₽.

Итог("Сумма") возвращает сумму колонки без изменения таблицы. Работает только для числовых значений.

Отбор: Скопировать с параметрами

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

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

Первый параметр — структура отбора или массив строк, второй — список колонок для новой таблицы. Без параметров Скопировать() даёт полную копию.

Условия «больше/меньше» структурой не задать: отбор сравнивает только на равенство. Для них пройдите циклом и соберите нужные строки в массив, а затем передайте его в Скопировать(МассивСтрок).

Поиск строк в таблице значений и индексы

Найти — первая подходящая строка

Строка = ТЗ.Найти(Товар, "Номенклатура");
Если Строка <> Неопределено Тогда
	Строка.Количество = Строка.Количество + 1;
КонецЕсли;

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

НайтиСтроки — все строки по условию

Условие = Новый Структура("Номенклатура, Склад", Товар, Склад);
Найденные = ТЗ.НайтиСтроки(Условие);
Для Каждого Строка Из Найденные Цикл
	Строка.Комментарий = "Проверено";
КонецЦикла;

Метод возвращает массив ссылок на строки исходной таблицы. Изменения в них сразу попадают в таблицу. Пустой массив — значит, ничего не найдено.

Индексы

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

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

Индекс ускоряет НайтиСтроки, если набор полей условия совпадает с полями индекса. Плата — память и время на перестройку индекса при каждом изменении строк. Поэтому индекс добавляют после заполнения таблицы, а не до.

СценарийЧто выбрать
Одна проверка «есть ли строка»Найти
Несколько строк по 1–3 полям, таблица небольшаяНайтиСтроки без индекса
Тысячи поисков по большой таблицеНайтиСтроки + индекс
Ключ один и уникаленСоответствие вместо таблицы

Перебор строк и безопасное удаление

Обычный перебор — Для Каждого:

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

Цикл по индексу нужен, когда важна позиция строки:

Для Инд = 0 По ТЗ.Количество() - 1 Цикл
	Строка = ТЗ[Инд];
КонецЦикла;

Удалять строки внутри прямого цикла нельзя — после удаления индексы сдвигаются, и каждая следующая строка пропускается. Идите с конца:

Всего = ТЗ.Количество();
Для Сч = 1 По Всего Цикл
	Строка = ТЗ[Всего - Сч];
	Если Строка.Количество = 0 Тогда
		ТЗ.Удалить(Строка);
	КонецЕсли;
КонецЦикла;

Граница Всего вычисляется один раз до цикла. При удалении строки с индексом Всего - Сч все строки перед ней сохраняют свои индексы, так что обход корректен. Второй вариант — сначала собрать удаляемые строки через НайтиСтроки, затем удалить их обычным циклом по полученному массиву.

Выгрузка в массив, передача в запрос и в форму

В массив

ВыгрузитьКолонку("Номенклатура") возвращает массив значений колонки в порядке строк. Дубли остаются. Если нужны уникальные значения — сверните копию:

Копия = ТЗ.Скопировать(, "Номенклатура");
Копия.Свернуть("Номенклатура");
МассивТоваров = Копия.ВыгрузитьКолонку("Номенклатура");

Массив удобно передавать в запрос условием ГДЕ Номенклатура В (&Список).

Массив структур

Для обмена через JSON или передачи на клиент таблицу превращают в массив структур:

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

Таблица как источник данных запроса

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

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Т.Номенклатура КАК Номенклатура,
|	Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
|	&ТЗ КАК Т";
Запрос.УстановитьПараметр("ТЗ", ТЗ);
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Выполнить();

Выбирать напрямую из параметра можно только в запросе с ПОМЕСТИТЬ. Соединения и отборы делают уже со временной таблицей ВТ_Товары в следующем пакете.

Управляемая форма

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

&НаСервере
Процедура ПересчитатьНаСервере()
	ТЗ = РеквизитФормыВЗначение("Остатки");
	ТЗ.Сортировать("Сумма Убыв");
	ЗначениеВРеквизитФормы(ТЗ, "Остатки");
КонецПроцедуры

Для табличной части объекта на форме вместо этого обычно работают с Объект.Товары.Выгрузить() и Объект.Товары.Загрузить(ТЗ) на сервере.

Хранение и вывод

  • 💾 сохранить между сеансами — Новый ХранилищеЗначения(ТЗ) в реквизите типа «Хранилище значения»;
  • 💾 в строку для отладки или передачи — ЗначениеВСтрокуВнутр(ТЗ) и обратно ЗначениеИзСтрокиВнутр();
  • 🖥 быстро посмотреть в отладчике — откройте переменную в окне вычисления выражения, таблица показывается с колонками.

Ошибки при работе с таблицей значений и как ускорить код

Диагностика: выберите симптом

Что тормозит и как исправить

  • 🐢 Найти внутри цикла по другой таблице. На 20 000 × 20 000 строк это 400 млн сравнений в худшем случае. Решение — индекс и НайтиСтроки или Соответствие с ключом.
  • 🐢 Обработка в цикле того, что умеет запрос. Группировку, соединение и отбор по условиям «больше/меньше» быстрее сделать запросом с временной таблицей из ТЗ.
  • 🐢 Индекс до заполнения. С индексом каждое Добавить() обновляет его. Добавляйте его после загрузки строк.
  • 🐢 Лишние колонки. Свернуть и Скопировать с перечнем колонок уменьшают объём данных, если дальше нужны только два-три поля.
  • 🐢 Передача огромной таблицы клиенту через реквизит формы. Каждая серверная операция гоняет данные туда и обратно. Держите на форме только то, что пользователь видит.

Для подсчёта суммы по одному товару не нужен цикл: ТЗ.Скопировать(Новый Структура("Номенклатура", Товар)).Итог("Сумма").

Частые вопросы по таблице значений

Чем таблица значений отличается от табличной части?
Табличная часть хранится в базе внутри объекта. Таблица значений живёт только в памяти, пока работает код, и в базу сама не записывается.
Как узнать, есть ли в таблице колонка?
Вызовите ТЗ.Колонки.Найти("Имя"). Метод вернёт колонку или Неопределено, если её нет.
Можно ли удалить или переименовать колонку?
Да. Удаление — ТЗ.Колонки.Удалить("Комментарий"). Переименование — через свойство Имя найденной колонки: ТЗ.Колонки.Найти("Сумма").Имя = "СуммаСНДС".
Как проверить, что таблица пустая?
Сравните ТЗ.Количество() с нулём. Сам объект не равен Неопределено, даже если строк нет.
Как вывести таблицу значений пользователю без формы?
Проще всего сформировать табличный документ или передать данные в отчёт на СКД с внешним набором данных. Для отладки хватит окна вычисления выражения.