Как работать с таблицей значений во встроенном языке 1С
Содержание
Таблица значений (объект ТаблицаЗначений) — это коллекция в памяти, у которой есть типизированные колонки и строки, как у таблицы в отчёте. В неё удобно складывать промежуточные данные, искать по значению, сортировать и считать итоги без обращения к базе.
Создаётся через Новый ТаблицаЗначений, доступна на сервере, в толстом клиенте и во внешнем соединении. На тонком и веб-клиенте напрямую создать её нельзя — там работают через реквизит формы. Ниже разберём структуру объекта, его методы и приёмы, которыми пользуются при обработке данных в коде.
Чем таблица значений отличается от массива и структуры
Выбор коллекции зависит от формы данных. Если вам нужен просто список — хватит массива. Если набор именованных полей одной записи — структура. Таблица значений нужна, когда записей много и у каждой одинаковый набор полей.
| Коллекция | Что хранит | Когда брать |
|---|---|---|
Массив | Одномерный список значений | Перечень без полей: список ссылок, строк |
Структура | Пары «ключ — значение», ключ = имя | Одна запись с именованными полями |
Соответствие | Пары «ключ — значение», ключ любой | Быстрый доступ по произвольному ключу |
СписокЗначений | Значения + представление + пометка | Выбор пользователя, меню |
ТаблицаЗначений | Строки × типизированные колонки | Набор однотипных записей: поиск, сортировка, итоги |
Главное отличие таблицы — она двумерная. У неё есть коллекция колонок (Колонки) и коллекция строк. К значению обращаются по строке и имени колонки: Строка.Сумма. Встроенные методы поиска, сортировки и свёртки экономят десятки строк ручного кода.
Ещё один довод в пользу таблицы — типизация. У каждой колонки можно задать тип, и платформа не даст записать в числовую колонку строку. У массива и соответствия такого контроля нет: туда ложится любое значение, и ошибку вы поймаете только при расчёте. Поэтому для данных, которые дальше суммируются или сравниваются, таблица надёжнее.
Для иерархии (строки внутри строк) есть отдельный объект — ДеревоЗначений. Если вложенность не нужна, берите плоскую таблицу: она проще и быстрее.
Как создать таблицу и описать её колонки
Колонка без явного типа принимает значения любого типа. Чтобы 1С контролировала данные и правильно считала итоги, тип лучше задавать через ОписаниеТипов.
Товары = Новый ТаблицаЗначений;
// Колонка без типа — примет что угодно
Товары.Колонки.Добавить("Номенклатура");
// Колонка с типом «Число», 15 знаков, 2 после запятой
ОписаниеЧисла = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
Товары.Колонки.Добавить("Количество", ОписаниеЧисла, "Кол-во");
Товары.Колонки.Добавить("Цена", ОписаниеЧисла);
Товары.Колонки.Добавить("Сумма", ОписаниеЧисла, "Сумма, руб");
У метода Добавить колонки три полезных параметра:
- 🏷️ имя — по нему обращаются в коде, латиница или кириллица без пробелов;
- 📐 тип — объект
ОписаниеТипов, можно перечислить несколько типов через запятую в строке; - 📝 заголовок — то, что увидит пользователь, если таблица выводится на форму.
Проверить состав колонок можно обращением по имени или индексу: Товары.Колонки.Количество(), Товары.Колонки[0].Имя, Товары.Колонки.Найти("Цена"). Если колонки с таким именем нет, Найти вернёт Неопределено — это удобно для проверки перед добавлением.
В одной колонке можно разрешить несколько типов — перечислите их в первом параметре ОписаниеТипов через запятую:
ОписаниеСсылки = Новый ОписаниеТипов("СправочникСсылка.Номенклатура, СправочникСсылка.Услуги");
Строки.Колонки.Добавить("Объект", ОписаниеСсылки);
На практике таблицу редко собирают колонку за колонкой вручную. Чаще её получают готовой из результата запроса — метод Выгрузить возвращает заполненную ТаблицаЗначений с колонками по именам полей выборки:
Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура, Количество, Сумма ИЗ ...");
Товары = Запрос.Выполнить().Выгрузить();
Такой способ удобен тем, что типы колонок платформа проставляет сама по типам полей запроса — вручную описывать их не нужно.
Добавление, изменение и удаление строк
Новая строка создаётся методом Добавить (в конец) или Вставить (по индексу). Оба возвращают объект строки, которому тут же присваивают значения колонок.
НоваяСтрока = Товары.Добавить();
НоваяСтрока.Номенклатура = "Болт М8";
НоваяСтрока.Количество = 100;
НоваяСтрока.Цена = 12.5;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена; // 1250
Когда исходные данные лежат в другой структуре или строке таблицы, перенести совпадающие по имени поля помогает ЗаполнитьЗначенияСвойств:
ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеИзЗапроса);
Чтобы добавить строку не в конец, а в нужную позицию, используйте Вставить(Индекс) — остальные строки сдвинутся вниз. Переставить уже существующую строку помогает Сдвинуть(Строка, Смещение): положительное смещение двигает строку вниз, отрицательное — вверх.
ПерваяСтрока = Товары.Вставить(0); // вставить в начало таблицы
ПерваяСтрока.Номенклатура = "Итоговая строка";
Товары.Сдвинуть(ПерваяСтрока, 2); // опустить её на 2 позиции вниз
Перебор строк — через Для Каждого. Внутри цикла значения доступны по имени колонки:
ИтогоСумма = 0;
Для Каждого Строка Из Товары Цикл
ИтогоСумма = ИтогоСумма + Строка.Сумма;
КонецЦикла;
Не удаляйте строки внутри цикла Для Каждого по той же таблице — это нарушает перебор. Безопасный приём: сначала собрать ненужные строки методом НайтиСтроки, потом удалить их отдельным циклом.
НаУдаление = Товары.НайтиСтроки(Новый Структура("Количество", 0));
Для Каждого Строка Из НаУдаление Цикл
Товары.Удалить(Строка);
КонецЦикла;
Полностью стереть содержимое, оставив колонки, — метод Очистить(). Узнать номер строки в таблице — Товары.Индекс(Строка), получить строку по номеру — Товары[0].
Поиск, отбор и сортировка данных
Для поиска есть два разных метода, их часто путают.
Найти(Значение, "Колонки") ищет одно значение в указанных колонках и возвращает первую подходящую строку или Неопределено. Проверяйте результат перед обращением к полям:
Строка = Товары.Найти("Болт М8", "Номенклатура");
Если Строка <> Неопределено Тогда
Строка.Количество = Строка.Количество + 50;
КонецЕсли;
НайтиСтроки(Отбор) возвращает массив всех строк, подходящих под отбор. Отбор задаётся структурой, где ключ — имя колонки, значение — искомое:
Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", "Болт М8");
Отбор.Вставить("Цена", 12.5);
Строки = Товары.НайтиСтроки(Отбор); // массив строк, может быть пустым
Сортировка выполняется методом Сортировать. В строке перечисляют колонки с направлением: Возр (по возрастанию, по умолчанию) или Убыв.
Товары.Сортировать("Сумма Убыв, Номенклатура Возр");
Если НайтиСтроки вызывается в цикле по большой таблице, добавьте индекс: Товары.Индексы.Добавить("Номенклатура"). Поиск по проиндексированной колонке идёт заметно быстрее линейного перебора.
Итоги, группировка и свёртка строк
Чтобы не считать сумму колонки вручную циклом, есть метод Итог:
ОбщаяСумма = Товары.Итог("Сумма"); // сумма всех значений колонки
Группировку с суммированием делает Свернуть. Первый параметр — колонки группировки, второй — суммируемые числовые колонки. После свёртки остаётся по одной строке на каждое сочетание значений в колонках группировки:
// Схлопнуть до строк по номенклатуре, просуммировав количество и сумму
Товары.Свернуть("Номенклатура", "Количество, Сумма");
Разберём на примере. Пусть в таблице по три строки на каждый из двух складов. После Товары.Свернуть("Склад", "Сумма") останется две строки — по одной на склад, и в колонке Сумма будет итог по складу. Колонки, не попавшие ни в группировку, ни в суммирование (например, Цена), из результата исчезнут — Свернуть оставляет только перечисленные. Если нужно их сохранить, скопируйте таблицу до свёртки или не включайте такие колонки в исходный набор.
Частый сценарий — сумма по отбору: отобрать строки одного склада и сложить их. Здесь выручает комбинация Скопировать и Итог:
ПоСкладу = Товары.Скопировать(Новый Структура("Склад", ВыбранныйСклад));
СуммаПоСкладу = ПоСкладу.Итог("Сумма");
Если нужно выгрузить все значения одной колонки в массив (например, собрать ссылки, чтобы передать их параметром в другой запрос) — используйте ВыгрузитьКолонку, а записать массив обратно в колонку — ЗагрузитьКолонку. Порядок значений в массиве соответствует порядку строк:
МассивНоменклатуры = Товары.ВыгрузитьКолонку("Номенклатура");
// ...получили цены отдельно и кладём обратно в том же порядке строк
Товары.ЗагрузитьКолонку(МассивЦен, "Цена");
Для суммирования по нескольким срезам сразу есть приём: сначала НайтиСтроки по отбору, затем Итог уже отобранной копии. Либо скопировать нужный срез в отдельную таблицу и работать с ней, не трогая исходную.
Скопировать часть таблицы по отбору помогает Скопировать. Можно задать и отбор, и состав колонок результата:
ТолькоДорогие = Товары.Скопировать(Новый Структура("Цена", 1000), "Номенклатура, Цена");
Таблица значений на управляемой форме
На управляемой форме (интерфейс «Такси») таблица отображается через реквизит формы типа ТаблицаЗначений, к которому привязан элемент-таблица. На клиенте вы работаете не с самим объектом, а с данными формы.
Порядок такой:
- В конфигураторе добавьте реквизит формы типа
ТаблицаЗначенийи опишите его колонки. - Перетащите реквизит в дерево элементов формы — появится таблица на экране.
- В модуле формы обрабатывайте данные — на сервере через настоящий объект, на клиенте через данные формы.
Чтобы получить из реквизита формы полноценный объект ТаблицаЗначений и обработать его серверными методами, применяют пару методов формы:
&НаСервере
Процедура ПересчитатьНаСервере()
ТЗ = РеквизитФормыВЗначение("Товары"); // реквизит формы -> ТаблицаЗначений
ТЗ.Сортировать("Сумма Убыв");
ТЗ.Свернуть("Номенклатура", "Сумма");
ЗначениеВРеквизитФормы(ТЗ, "Товары"); // обратно в реквизит формы
КонецПроцедуры
Напрямую передать ТаблицаЗначений параметром в клиентскую процедуру нельзя — на тонком клиенте этого типа нет. Для передачи с сервера на клиент используйте реквизит формы или временное хранилище (ПоместитьВоВременноеХранилище), а на клиенте оперируйте строками данных формы.
Для разовой выдачи данных пользователю таблицу удобно вывести в табличный документ или сразу в реквизит формы с привязанным элементом — тогда пользователь увидит её на экране с заголовками колонок. Именно заголовок колонки (третий параметр Колонки.Добавить), а не внутреннее имя, покажется в шапке таблицы на форме.
Если таблицу нужно открыть пользователю как отдельное окно, не создавая под неё реквизит формы заранее, можно вывести её в табличный документ построчно и показать. Но для регулярной работы с данными привязка к реквизиту формы удобнее: пользователь сможет сортировать и отбирать строки штатными средствами платформы.
Частые ошибки и ускорение работы с таблицей
Самые частые ошибки связаны с обращением к несуществующим колонкам и с результатом поиска Неопределено. Разбор по симптому:
Колонки.Добавить. Имена чувствительны к написанию, но не к регистру.Найти вернул Неопределено, а вы сразу обратились к полю строки. Проверяйте результат: Если Строка <> Неопределено Тогда.Товары[Н] или Колонки[Н] с номером, которого нет. Контролируйте границы через Количество().Что ускоряет код при больших объёмах:
- 🚀 Индексы. Перед множественными
НайтиСтрокидобавьтеИндексы.Добавить("Колонка")— поиск пойдёт по индексу, а не перебором. - 🧮 Свернуть вместо ручной группировки. Один вызов
Свернутьбыстрее цикла с накоплением во вспомогательной таблице. - 📤 ВыгрузитьКолонку вместо цикла, когда нужен только массив значений одной колонки.
- 🗄️ Запрос вместо таблицы в памяти. Если данных десятки тысяч строк и их можно отобрать и сгруппировать запросом к базе — делайте это на стороне СУБД, а в таблицу значений выгружайте уже готовый результат.
- 🔁 Не меняйте число строк в прямом переборе. Удаление и вставку внутри
Для Каждогопо той же таблице выносите в отдельный цикл по массиву изНайтиСтроки.
Ещё одна тонкость: колонка без указанного типа хранит значения любого типа, и Итог по ней отработает только если там действительно числа. Задавайте тип колонки сразу — это и защита от мусора, и подсказка платформе при расчётах.
Отдельно про память. Таблица значений целиком лежит в оперативной памяти сеанса. Если выгрузить в неё миллионы строк, это ударит по памяти рабочего процесса на сервере. Для таких объёмов фильтруйте и группируйте данные запросом в СУБД, а в таблицу значений забирайте уже сжатый результат — так вы переносите тяжёлую работу на сторону базы, где она выполняется эффективнее.
И про отладку: чтобы посмотреть содержимое таблицы в точке останова, в отладчике откройте значение переменной — таблица показывается с колонками и строками, как на форме. Это быстрее, чем выводить строки в сообщения вручную.
Частые вопросы
Чем таблица значений отличается от дерева значений?
ДеревоЗначений) поддерживает вложенные строки — иерархию «родитель → подчинённые». У таблицы значений строки плоские, без вложенности. Если иерархия не нужна, берите таблицу: она проще и работает быстрее.Можно ли создать таблицу значений на тонком клиенте?
Новый ТаблицаЗначений на тонком и веб-клиенте недоступен. Создавайте объект на сервере, а на форме используйте реквизит типа ТаблицаЗначений с привязанным элементом-таблицей.Как передать таблицу значений с сервера на клиент?
ЗначениеВРеквизитФормы и РеквизитФормыВЗначение) либо через временное хранилище. Параметром в клиентскую процедуру этот тип не передаётся.Как быстро получить сумму по колонке?
Итог("ИмяКолонки") — он вернёт сумму числовой колонки без цикла. Для суммы по группам используйте Свернуть.Как скопировать только часть строк в новую таблицу?
Скопировать(Отбор, "Колонки") — первым параметром задаёте структуру-отбор, вторым перечисляете нужные колонки результата.