Работа с таблицей значений в 1С 8.3: от создания до выгрузки

· 9 мин

Содержание

Таблица значений — объект встроенного языка 1С для хранения данных в оперативной памяти в виде таблицы со строками и колонками. Создают её конструктором Новый ТаблицаЗначений, колонки добавляют методом Колонки.Добавить(), строки — методом Добавить(), а заполняют либо присваиванием по имени колонки, либо целиком из результата запроса.

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

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

Что такое таблица значений и из чего она состоит

Таблица значений (ТаблицаЗначений) — универсальная коллекция. У неё две вложенные коллекции:

  • 📊 Колонки — описание столбцов: имя, тип, заголовок, ширина;
  • 📄 строки (СтрокаТаблицыЗначений) — сами данные, доступ к ячейке идёт по имени колонки.

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

Таблица значений доступна и на сервере, и на клиенте, свободно передаётся через границу клиент-сервер как параметр метода. Единственное ограничение: напрямую положить её в реквизит управляемой формы нельзя — для формы нужна отдельная конвертация (об этом в разделе про ошибки).

Перебрать описания колонок можно так же, как строки, — коллекция Колонки доступна для обхода, а Колонки.Количество() вернёт их число:

Для Каждого Колонка Из ТЗ.Колонки Цикл
    Сообщить(Колонка.Имя);
КонецЦикла;

Таблицу значений часто путают с деревом значений (ДеревоЗначений). Разница одна: дерево хранит строки иерархически (строка может иметь подчинённые), таблица — плоский список. Для хранения пар «ключ → значение» без строк и колонок берут Соответствие, а не таблицу.

Как создать таблицу значений и добавить колонки

Минимальная заготовка — одна строка:

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

Пустая таблица без колонок бесполезна, поэтому сразу описываем столбцы. Метод Колонки.Добавить() принимает четыре параметра: имя, тип значения, заголовок и ширину. Обязательно только имя, но типизировать колонки стоит всегда — так 1С контролирует данные и быстрее работает с ними.

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

Тип задаётся объектом ОписаниеТипов. Для чисел добавляют КвалификаторыЧисла(разрядность, дробнаяЧасть) — здесь 15 знаков всего, из них 2 или 3 после запятой. Для строк по аналогии используют КвалификаторыСтроки(длина).

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

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

Имя колонки должно быть уникальным — повторное добавление с тем же именем вызовет ошибку. Проверить, есть ли уже такая колонка, можно через ТЗ.Колонки.Найти("Имя") <> Неопределено.

Способы добавить и заполнить строки

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

НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = ВыбранныйТовар;
НоваяСтрока.Количество  = 10;
НоваяСтрока.Цена        = 150;

Нужно вставить строку не в конец, а в определённую позицию — есть Вставить(индекс). Например, ТЗ.Вставить(0) добавит строку в самое начало.

К ячейке можно обращаться тремя способами — по имени, по строковому ключу и по индексу колонки:

НоваяСтрока.Цена;        // по имени — читаемее всего
НоваяСтрока["Цена"];     // строкой — когда имя колонки в переменной
НоваяСтрока[2];          // по индексу — быстро, но ломается при смене порядка колонок

Заполнение из запроса — самый частый сценарий. Метод Выгрузить() результата запроса сам создаёт таблицу значений, у которой колонки совпадают с полями выборки:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Товары.Номенклатура КАК Номенклатура,
|    Товары.Количество   КАК Количество,
|    Товары.Цена         КАК Цена
|ИЗ
|    Документ.РеализацияТоваровУслуг.Товары КАК Товары";

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

Отдельную колонку наполняют массивом через ЗагрузитьКолонку(Массив, "Имя"), а выгружают в массив — через ВыгрузитьКолонку("Имя"):

МассивЦен = ТЗ.ВыгрузитьКолонку("Цена");

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

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

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

Когда строку нужно наполнить из объекта с такими же именами реквизитов — документа, элемента справочника, другой структуры — пригодится глобальная функция ЗаполнитьЗначенияСвойств. Она копирует значения всех совпадающих по имени свойств за один вызов:

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

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

Вычисляемые значения: итоги и свёртка

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

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

Для подведения итога по колонке есть готовый метод Итог() — он складывает все числовые значения столбца:

ВсегоСумма = ТЗ.Итог("Сумма");

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

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

После свёртки в таблице останется по одной строке на каждую уникальную номенклатуру, а количество и сумма сложатся. Колонки, не попавшие ни в группировку, ни в суммирование, удаляются.

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

Этих трёх приёмов — обход циклом, Итог и Свернуть — хватает для большинства расчётов без обращения к запросам.

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

Сортировка задаётся строкой с именами колонок и направлением (Возр или Убыв):

ТЗ.Сортировать("Сумма Убыв, Номенклатура Возр");

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

Поиск первой подходящей строки — метод Найти(). Он возвращает строку или Неопределено, если ничего не нашлось:

Строка = ТЗ.Найти(ИскомыйТовар, "Номенклатура");
Если Строка <> Неопределено Тогда
    Сообщить("Цена: " + Строка.Цена);
КонецЕсли;

Все строки по условию возвращает НайтиСтроки() — на вход структура отбора «имя колонки → значение», на выходе массив строк:

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

Для Каждого Строка Из НайденныеСтроки Цикл
    // обрабатываем каждую найденную строку
КонецЦикла;

Отбор подмножества в новую таблицу делает Скопировать(). Ему можно передать и структуру отбора, и список нужных колонок:

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

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

Позиция конкретной строки возвращается методом Индекс() — это полезно, когда нужно понять, где строка находится, или переместить её:

ТекущийИндекс = ТЗ.Индекс(Строка);   // порядковый номер строки, с нуля
ТЗ.Сдвинуть(Строка, -1);             // поднять строку на одну позицию вверх

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

Производительность на больших объёмах

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

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

  2. Фильтрация и агрегация на стороне запроса. Если данные берутся из базы, не выгружайте всё, а потом отбирайте в коде — наложите отбор и группировку в тексте запроса. Чем меньше строк доедет до таблицы, тем быстрее.

  3. Удаление строк — через найденный массив или обратным обходом. Удалять строки прямо в цикле Для Каждого нельзя: коллекция меняется, часть строк пропускается. Безопасный способ — сначала собрать строки, потом удалить:

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

Либо обойти по индексу от конца к началу циклом Пока — тогда удаление не сбивает нумерацию оставшихся строк:

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

Полностью очистить таблицу от строк, сохранив структуру колонок, — метод Очистить(). Он быстрее, чем удаление строк по одной.

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

Большинство проблем сводится к нескольким повторяющимся ситуациям.

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

Ещё одна частая причина сбоев — обращение к ячейке по индексу колонки (Строка[2]) после того, как порядок колонок изменился. По возможности обращайтесь по имени: код переживёт перестановку столбцов.

Проверьте себя
Что вернёт метод `Найти()`, если строка по условию не нашлась?
Каким методом сгруппировать строки и сложить числовые колонки?
Можно ли удалять строки прямо в цикле `Для Каждого Из ТЗ`?

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

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