Таблица значений в 1С: создание, поиск строк, передача в запрос и на форму
Таблица значений нужна почти в любой обработке данных на 1С. Здесь собраны готовые фрагменты кода для всех частых операций: от создания колонок с типами до передачи таблицы в запрос и работы с ней на управляемой форме, а также ошибки, которые встречаются чаще всего.
Задача: собрать и обработать данные в памяти
Таблица значений — универсальная коллекция встроенного языка, похожая на таблицу базы данных, но живущая только в памяти. В неё выгружается результат запроса, в ней накапливают данные из файла или веб-сервиса, по ней ищут, сортируют и считают итоги, а потом загружают в табличную часть документа или передают обратно в запрос.
Ниже — полный цикл работы с таблицей значений программно: создание с типизированными колонками, добавление строк, поиск, сортировка, свёртка, удаление строк в цикле, преобразования «массив ↔ таблица», передача в запрос и вывод на управляемую форму.
Создание таблицы значений и колонки с типами
Таблица создаётся конструктором Новый ТаблицаЗначений, колонки добавляются через коллекцию Колонки. Второй параметр метода Колонки.Добавить() — ОписаниеТипов. Его можно не указывать, но тогда колонка примет значения любого типа, а это создаёт проблемы при передаче таблицы в запрос и при выводе на форму.
&НаСервере
Функция НоваяТаблицаТоваров()
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Таблица.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Таблица.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
Таблица.Колонки.Добавить("ДатаОтгрузки", Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)));
Возврат Таблица;
КонецФункции
Порядок параметров конструктора ОписаниеТипов: типы, квалификаторы числа, квалификаторы строки, квалификаторы даты. Поэтому для строки и даты пропущенные параметры отмечаются запятыми. Если колонке нужны несколько типов, перечислите их через запятую в одной строке: Новый ОписаниеТипов("СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица").
Добавить колонку в таблицу значений можно и после заполнения: новая колонка появится у всех существующих строк со значением по умолчанию для своего типа. Колонку вставляют на нужную позицию методом Колонки.Вставить(Индекс, Имя, Тип), удаляют — Колонки.Удалить(), проверяют наличие — Колонки.Найти("Имя"), который вернёт Неопределено, если колонки нет.
Как добавить строку в таблицу значений
Метод Добавить() создаёт пустую строку в конце таблицы и возвращает её — дальше значения присваиваются по именам колонок. Если данные уже лежат в структуре или в строке другой коллекции с теми же именами, удобнее ЗаполнитьЗначенияСвойств().
Таблица = НоваяТаблицаТоваров();
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00-0001");
НоваяСтрока.Склад = Справочники.Склады.Основной;
НоваяСтрока.Количество = 10;
НоваяСтрока.Сумма = 1500;
// Заполнение из структуры с совпадающими именами
Данные = Новый Структура("Номенклатура, Количество, Сумма", Товар, 2, 300);
ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Данные);
// Вставка строки на конкретную позицию
ПерваяСтрока = Таблица.Вставить(0);
Обход строк и доступ к значениям
Таблицу перебирают циклом Для Каждого СтрокаТаблицы Из Таблица Цикл. К значению колонки обращаются через точку (СтрокаТаблицы.Сумма) или по имени в квадратных скобках (СтрокаТаблицы["Сумма"]) — второй вариант нужен, когда имя колонки хранится в переменной. Строку можно получить и по индексу: Таблица[0] — первая строка, Таблица.Количество() — их число, а Таблица.Индекс(Строка) возвращает позицию строки.
Для Каждого СтрокаТаблицы Из Таблица Цикл
СтрокаТаблицы.Сумма = Окр(СтрокаТаблицы.Сумма, 2);
Для Каждого Колонка Из Таблица.Колонки Цикл
Значение = СтрокаТаблицы[Колонка.Имя];
КонецЦикла;
КонецЦикла;
Изменять значения в строках внутри такого цикла безопасно — проблемы начинаются только при удалении строк, о чём ниже. Для быстрого пересчёта колонки целиком удобна пара ВыгрузитьКолонку() и ЗагрузитьКолонку(): выгрузили массив, обработали, загрузили обратно в ту же или другую колонку.
Поиск в таблице значений: Найти и НайтиСтроки
Для поиска есть два метода с разным назначением.
Найти(Значение, Колонки) ищет одно значение и возвращает первую подходящую строку или Неопределено. Второй параметр — имена колонок через запятую, по которым идёт поиск; если его не указать, значение ищется во всех колонках.
НайтиСтроки(Отбор) принимает структуру с условиями «колонка — значение», все условия объединяются по «И». Возвращается массив строк — пустой, если ничего не найдено.
// Одна строка по значению
Строка = Таблица.Найти(Товар, "Номенклатура");
Если Строка <> Неопределено Тогда
Строка.Количество = Строка.Количество + 1;
КонецЕсли;
// Все строки по нескольким условиям
Отбор = Новый Структура("Номенклатура, Склад", Товар, Справочники.Склады.Основной);
НайденныеСтроки = Таблица.НайтиСтроки(Отбор);
Для Каждого СтрокаТаблицы Из НайденныеСтроки Цикл
СтрокаТаблицы.Комментарий = "Проверено";
КонецЦикла;
При частом поиске по одной и той же колонке создайте индекс: Таблица.Индексы.Добавить("Номенклатура, Склад"). На таблицах в десятки тысяч строк НайтиСтроки() с индексом работает на порядки быстрее перебора циклом.
Сортировка, свёртка, итоги и копирование
// Сортировка по нескольким колонкам
Таблица.Сортировать("Склад, Сумма Убыв");
// Группировка: колонки группировки и суммируемые колонки
Таблица.Свернуть("Номенклатура, Склад", "Количество, Сумма");
// Итог по числовой колонке
ВсегоСумма = Таблица.Итог("Сумма");
// Копия только части строк и колонок
ОтборСклада = Новый Структура("Склад", Справочники.Склады.Основной);
ТаблицаСклада = Таблица.Скопировать(ОтборСклада, "Номенклатура, Количество");
// Уникальные значения колонки
Уникальные = Таблица.Скопировать(, "Номенклатура");
Уникальные.Свернуть("Номенклатура");
МассивТоваров = Уникальные.ВыгрузитьКолонку("Номенклатура");
Свернуть() удаляет колонки, не перечисленные ни в одном из параметров, — если они понадобятся, сверните копию. Скопировать() принимает первым параметром либо структуру отбора, либо массив строк, а без параметров создаёт полную копию с той же структурой. Пустую таблицу с теми же колонками даёт СкопироватьКолонки().
Удаление строк в цикле
Классическая ошибка — удалять строки внутри Для Каждого по самой таблице: после удаления индексы сдвигаются, и часть строк пропускается. Есть два надёжных способа.
// Способ 1: обратный обход по индексу
Индекс = Таблица.Количество() - 1;
Пока Индекс >= 0 Цикл
Если Таблица[Индекс].Количество = 0 Тогда
Таблица.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
// Способ 2: сначала найти, потом удалить
СтрокиКУдалению = Таблица.НайтиСтроки(Новый Структура("Количество", 0));
Для Каждого СтрокаКУдалению Из СтрокиКУдалению Цикл
Таблица.Удалить(СтрокаКУдалению);
КонецЦикла;
Во втором случае цикл идёт по отдельному массиву, поэтому удаление из таблицы его не ломает. Полностью очищает таблицу метод Очистить() — колонки при этом сохраняются.
Массив в таблицу значений и обратно
Простой массив значений загружается в колонку методом ЗагрузитьКолонку(), но строк в таблице должно быть не меньше, чем элементов. Поэтому сначала добавляют нужное количество строк. Массив структур удобнее переносить через ЗаполнитьЗначенияСвойств().
// Массив значений -> колонка таблицы
Функция МассивВТаблицуЗначений(Массив, ИмяКолонки, ОписаниеТипа) Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить(ИмяКолонки, ОписаниеТипа);
Для Каждого Элемент Из Массив Цикл
НоваяСтрока = Таблица.Добавить();
НоваяСтрока[ИмяКолонки] = Элемент;
КонецЦикла;
Возврат Таблица;
КонецФункции
// Массив структур -> таблица значений (колонки по ключам первой структуры)
Функция МассивСтруктурВТаблицуЗначений(МассивСтруктур) Экспорт
Таблица = Новый ТаблицаЗначений;
Если МассивСтруктур.Количество() = 0 Тогда
Возврат Таблица;
КонецЕсли;
Для Каждого КлючИЗначение Из МассивСтруктур[0] Цикл
Таблица.Колонки.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
Для Каждого ЭлементМассива Из МассивСтруктур Цикл
ЗаполнитьЗначенияСвойств(Таблица.Добавить(), ЭлементМассива);
КонецЦикла;
Возврат Таблица;
КонецФункции
// Таблица значений -> массив структур
Функция ТаблицаВМассивСтруктур(Таблица) Экспорт
ИменаКолонок = "";
Для Каждого Колонка Из Таблица.Колонки Цикл
ИменаКолонок = ИменаКолонок + ?(ИменаКолонок = "", "", ",") + Колонка.Имя;
КонецЦикла;
Результат = Новый Массив;
Для Каждого СтрокаТаблицы Из Таблица Цикл
Элемент = Новый Структура(ИменаКолонок);
ЗаполнитьЗначенияСвойств(Элемент, СтрокаТаблицы);
Результат.Добавить(Элемент);
КонецЦикла;
Возврат Результат;
КонецФункции
В конфигурациях на БСП для преобразования таблицы в массив структур есть готовая функция ОбщегоНазначения.ТаблицаЗначенийВМассив(). Массив структур удобен, когда данные нужно передать на клиент: сама таблица значений в тонком клиенте недоступна.
Обратите внимание: функция МассивСтруктурВТаблицуЗначений создаёт колонки без типов. Для дальнейшей передачи такой таблицы в запрос добавьте описания типов явно.
Таблица значений в запрос
Таблицу можно передать параметром и выбрать из неё данные, но только в запросе, который помещает результат во временную таблицу. Соединять параметр-таблицу с другими таблицами в том же запросе нельзя — сначала ПОМЕСТИТЬ, потом соединение в следующем запросе пакета.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| &ТаблицаТоваров КАК Т
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Товары.Номенклатура КАК Номенклатура,
| ВТ_Товары.Количество КАК Количество,
| ВТ_Товары.Номенклатура.Артикул КАК Артикул
|ИЗ
| ВТ_Товары КАК ВТ_Товары";
Запрос.УстановитьПараметр("ТаблицаТоваров", Таблица);
Результат = Запрос.Выполнить().Выгрузить();
Главное требование — у колонок, которые участвуют в запросе, должны быть заданы типы. Колонка без ОписаниеТипов вызывает ошибку «Тип не может быть выбран в запросе». Если таблица пришла из чужого кода без типов, создайте новую с типизированными колонками и перенесите данные через ЗаполнитьЗначенияСвойств().
Таблица значений на управляемой форме
На управляемой форме нельзя хранить настоящую ТаблицаЗначений: реквизит формы с этим типом на клиенте представлен коллекцией ДанныеФормыКоллекция. С ней работают так же — Добавить(), НайтиСтроки(), Удалить(), — но методы Свернуть(), Итог() и Скопировать() есть только у исходной таблицы. Поэтому для сложной обработки данные переводят в таблицу значений на сервере и обратно.
&НаСервере
Процедура СвернутьТоварыНаСервере()
// Реквизит формы "Товары" с типом ТаблицаЗначений
ТаблицаТоваров = РеквизитФормыВЗначение("Товары");
ТаблицаТоваров.Свернуть("Номенклатура", "Количество, Сумма");
ЗначениеВРеквизитФормы(ТаблицаТоваров, "Товары");
// Альтернатива для коллекции данных формы
// ТаблицаТоваров = Товары.Выгрузить();
// Товары.Загрузить(ТаблицаТоваров);
КонецПроцедуры
Колонки реквизита-таблицы задаются в конфигураторе как подчинённые реквизиты формы. Если колонки неизвестны заранее, их добавляют программно методом формы ИзменитьРеквизиты(), а затем создают элементы через Элементы.Добавить() — это отдельная тема, но важно помнить: без реквизита-колонки данные в нужное поле не попадут.
Частые ошибки
- Колонки без типов — проблемы в запросе, при выводе на форму и при сравнении чисел, пришедших строкой.
- Удаление строк в прямом цикле
Для Каждогопо самой таблице — часть строк остаётся. - Перебор циклом вместо
НайтиСтроки()с индексом на больших объёмах. - Вызов
Свернуть()на исходной таблице, когда остальные колонки ещё нужны. - Попытка передать таблицу значений на клиент — используйте реквизит формы или массив структур.
Шпаргалка по методам
| Задача | Метод | Результат |
|---|---|---|
| Добавить строку | Добавить(), Вставить(Индекс) | Новая строка |
| Найти одну строку | Найти(Значение, "Колонки") | Строка или Неопределено |
| Найти все строки | НайтиСтроки(Структура) | Массив строк |
| Сгруппировать | Свернуть(Группировки, Суммируемые) | Изменённая таблица |
| Сумма колонки | Итог("Колонка") | Число |
| Колонка в массив | ВыгрузитьКолонку("Колонка") | Массив |
| Часть таблицы | Скопировать(Отбор, "Колонки") | Новая таблица |
Актуальность для новых версий
Объект ТаблицаЗначений и перечисленные методы работают во всех версиях платформы 8.3, включая 8.3.2x. Таблица значений доступна только на сервере и в толстом клиенте; в тонком и веб-клиенте с данными работают через реквизит формы (ДанныеФормыКоллекция). Функция ОбщегоНазначения.ТаблицаЗначенийВМассив() есть только в конфигурациях на БСП — в остальных используйте собственную функцию из примера.
Частые вопросы
Как добавить колонку в таблицу значений с типом?
Вызовите Колонки.Добавить("Имя", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2))). Для строки и даты квалификаторы передаются третьим и четвёртым параметрами ОписаниеТипов.
Чем Найти() отличается от НайтиСтроки()?
Найти() ищет одно значение и возвращает первую подходящую строку или Неопределено. НайтиСтроки() принимает структуру с несколькими условиями и возвращает массив всех подходящих строк.
Почему таблица значений не передаётся в запрос?
У колонок, используемых в запросе, должны быть заданы типы, а выбирать из параметра-таблицы можно только в запросе с ПОМЕСТИТЬ. Соединения делают уже с полученной временной таблицей.
Как вывести таблицу значений на управляемую форму?
Создайте реквизит формы типа ТаблицаЗначений с нужными колонками и на сервере вызовите ЗначениеВРеквизитФормы(Таблица, "ИмяРеквизита"). Обратно данные получают методом РеквизитФормыВЗначение().