Как задать таблицу значений во встроенном языке 1С и работать с ней в памяти
Содержание
Таблица значений в 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 млн сравнений в худшем случае. Решение — индекс иНайтиСтрокиилиСоответствиес ключом. - 🐢 Обработка в цикле того, что умеет запрос. Группировку, соединение и отбор по условиям «больше/меньше» быстрее сделать запросом с временной таблицей из ТЗ.
- 🐢 Индекс до заполнения. С индексом каждое
Добавить()обновляет его. Добавляйте его после загрузки строк. - 🐢 Лишние колонки.
СвернутьиСкопироватьс перечнем колонок уменьшают объём данных, если дальше нужны только два-три поля. - 🐢 Передача огромной таблицы клиенту через реквизит формы. Каждая серверная операция гоняет данные туда и обратно. Держите на форме только то, что пользователь видит.
Для подсчёта суммы по одному товару не нужен цикл: ТЗ.Скопировать(Новый Структура("Номенклатура", Товар)).Итог("Сумма").
Частые вопросы по таблице значений
Чем таблица значений отличается от табличной части?
Как узнать, есть ли в таблице колонка?
ТЗ.Колонки.Найти("Имя"). Метод вернёт колонку или Неопределено, если её нет.Можно ли удалить или переименовать колонку?
ТЗ.Колонки.Удалить("Комментарий"). Переименование — через свойство Имя найденной колонки: ТЗ.Колонки.Найти("Сумма").Имя = "СуммаСНДС".Как проверить, что таблица пустая?
ТЗ.Количество() с нулём. Сам объект не равен Неопределено, даже если строк нет.