Чтение, поиск и отбор строк в ТаблицеЗначений: приёмы для кода 1С 8.3
Содержание
Значение из таблицы значений в 1С получают через строку и колонку: ТЗ[0].Сумма вернёт сумму из первой строки, ТЗ.Найти(Ссылка, "Номенклатура") — первую строку с нужным товаром, а ТЗ.ВыгрузитьКолонку("Сумма") — все значения колонки массивом. Нумерация строк начинается с нуля.
Остальное — вопрос задачи: перебрать всё, найти одну запись, отобрать группу строк или отдать таблицу в запрос. Ниже каждый способ с рабочим кодом для платформы 8.3. Объект ТаблицаЗначений доступен на сервере, в толстом клиенте и во внешнем соединении. В тонком клиенте и веб-клиенте его нет — это важно для кода управляемых форм.
Создание таблицы значений и заполнение строк
Таблицу создают конструктором, затем описывают колонки. Тип колонки задавать не обязательно, но лучше задать сразу: без него таблицу не получится нормально передать в запрос, а сравнения в поиске будут менее предсказуемыми.
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
ТЗ.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
Строку добавляют методом Добавить(). Он возвращает объект СтрокаТаблицыЗначений, в который пишут значения по именам колонок:
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = Товар;
НоваяСтрока.Склад = ОсновнойСклад;
НоваяСтрока.Количество = 10;
НоваяСтрока.Сумма = 1500;
Ещё три способа наполнения, которые сокращают код:
- 📥
ТЗ.Вставить(Индекс)— вставляет пустую строку в заданную позицию, а не в конец. - 📦
ТЗ.ЗагрузитьКолонку(Массив, "Количество")— заливает колонку из массива. Строк должно быть не меньше, чем элементов массива. - 🔁
ЗаполнитьЗначенияСвойств(НоваяСтрока, Источник)— переносит одноимённые поля из структуры, строки другой таблицы или выборки запроса.
Самый частый источник готовой таблицы — результат запроса: Запрос.Выполнить().Выгрузить(). Колонки и типы в этом случае уже заданы, описывать их вручную не нужно.
Перебор строк и доступ к значениям колонок
Цикл Для Каждого
Самый читаемый вариант. Подходит, когда нужно пройти все строки и что-то посчитать или прочитать:
ИтогоСумма = 0;
Для Каждого Строка Из ТЗ Цикл
ИтогоСумма = ИтогоСумма + Строка.Сумма;
КонецЦикла;
Для простой суммы цикл не нужен: ТЗ.Итог("Сумма") вернёт то же самое одной строкой.
Обращение по индексу
Когда нужен номер строки, сосед сверху или снизу, используют индекс от 0 до Количество() - 1:
Для Индекс = 0 По ТЗ.Количество() - 1 Цикл
Строка = ТЗ[Индекс];
Если Индекс > 0 И Строка.Номенклатура = ТЗ[Индекс - 1].Номенклатура Тогда
Строка.Комментарий = "Повтор товара";
КонецЕсли;
КонецЦикла;
ТЗ[Индекс] и ТЗ.Получить(Индекс) дают одно и то же. Обратная задача — узнать номер строки — решается методом ТЗ.Индекс(Строка).
Три способа прочитать ячейку
| Запись | Когда удобна |
|---|---|
Строка.Сумма | имя колонки известно при написании кода |
Строка["Сумма"] | имя колонки хранится в переменной или приходит параметром |
Строка[3] | обход колонок по порядку, например при выгрузке в файл |
Универсальный обход всех ячеек строки выглядит так:
Для Каждого Колонка Из ТЗ.Колонки Цикл
Значение = Строка[Колонка.Имя];
Сообщить(Колонка.Имя + ": " + Строка(Значение));
КонецЦикла;
Если колонка может отсутствовать, проверьте её заранее: ТЗ.Колонки.Найти("Сумма") вернёт Неопределено, когда такой колонки нет. Обращение к несуществующей колонке через точку прерывает выполнение ошибкой.
Все значения колонки сразу
ТЗ.ВыгрузитьКолонку("Номенклатура") возвращает массив значений в порядке строк. Его удобно передать в запрос как параметр для условия В (&Список). Дубли в массиве остаются; чтобы получить уникальные значения, сначала сверните копию таблицы:
Уникальные = ТЗ.Скопировать(, "Номенклатура");
Уникальные.Свернуть("Номенклатура");
СписокТоваров = Уникальные.ВыгрузитьКолонку("Номенклатура");
Выборка строк по условию: НайтиСтроки и Скопировать
У таблицы значений нет метода «Выборка» в том смысле, в каком он есть у результата запроса (Результат.Выбрать()). Отбор группы строк делают двумя методами.
НайтиСтроки — массив ссылок на строки
Метод принимает структуру, где ключи — имена колонок, а значения — то, что ищем. Условия объединяются через «И», сравнение только на равенство:
Отбор = Новый Структура("Склад, Номенклатура", ОсновнойСклад, Товар);
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
Строка.Комментарий = "Проверено";
КонецЦикла;
Результат — массив. Если ничего не нашлось, массив пустой, а не Неопределено. Элементы массива — те же строки исходной таблицы: изменение Строка.Комментарий меняет данные в ТЗ.
Скопировать с отбором — новая таблица
Когда нужна отдельная таблица с частью строк и колонок, используйте Скопировать:
Отбор = Новый Структура("Склад", ОсновнойСклад);
ТаблицаСклада = ТЗ.Скопировать(Отбор, "Номенклатура, Количество, Сумма");
Первый параметр может быть и массивом строк, например результатом НайтиСтроки. Второй — список нужных колонок через запятую. Без параметров метод копирует всю таблицу.
Условия «больше», «меньше», «содержит»
Структура отбора понимает только равенство. Для диапазонов есть два пути:
- Небольшая таблица — перебор циклом и сбор подходящих строк в массив.
- Большая таблица или сложное условие — передача таблицы в запрос (раздел ниже) и отбор через
ГДЕ.
КрупныеСтроки = Новый Массив;
Для Каждого Строка Из ТЗ Цикл
Если Строка.Сумма > 10000 Тогда
КрупныеСтроки.Добавить(Строка);
КонецЕсли;
КонецЦикла;
КрупныеПродажи = ТЗ.Скопировать(КрупныеСтроки);
Поиск одной записи методом Найти
Найти возвращает первую строку, где встретилось значение, или Неопределено:
Строка = ТЗ.Найти(Товар, "Номенклатура");
Если Строка <> Неопределено Тогда
Цена = Строка.Сумма / Строка.Количество;
КонецЕсли;
Второй параметр — список колонок через запятую, в которых искать. Если его не указать, поиск идёт по всем колонкам, и строка найдётся, даже если товар случайно оказался в другой колонке. Всегда указывайте колонку явно — так быстрее и без ложных совпадений.
Чем Найти отличается от НайтиСтроки:
| Метод | Что ищет | Что возвращает | Пусто |
|---|---|---|---|
Найти(Значение, Колонки) | одно значение в одной или нескольких колонках | первую строку | Неопределено |
НайтиСтроки(Структура) | сочетание значений в разных колонках | массив строк | пустой массив |
Деление в примере выше упадёт, если Количество равно нулю. Перед расчётом цены проверяйте делитель — в реальных данных нулевые строки встречаются чаще, чем кажется.
Типы значений должны совпадать. Число 5 и строка "5" — разные значения, поиск по колонке со строками не найдёт число. Для дат учитывается и время: '20260107' не равно '20260107120000'.
Индексы: ускорение поиска в больших таблицах
Без индекса Найти и НайтиСтроки просматривают таблицу от начала до конца. На сотне строк разницы не видно. На десятках тысяч строк, когда поиск вызывается внутри цикла, время растёт квадратично: 50 000 строк × 50 000 поисков — это уже минуты.
Индекс добавляют один раз, до серии поисков:
ТЗ.Индексы.Добавить("Номенклатура");
ТЗ.Индексы.Добавить("Склад, Номенклатура");
Правила, которые стоит держать в голове:
- 🎯 Индекс помогает, когда набор колонок в отборе совпадает с набором колонок индекса. Индекс по «Склад, Номенклатура» рассчитан на отбор по обеим колонкам сразу.
- 🐢 Каждый индекс замедляет добавление и изменение строк и занимает память. Не создавайте индекс «на всякий случай».
- 🧹 Индекс живёт, пока жива таблица. После
Скопироватьего нужно создавать заново. - 📏 На маленьких таблицах (до нескольких сотен строк) индекс почти ничего не даёт.
Типичный сценарий — сопоставление двух таблиц. Вместо поиска в цикле по второй таблице без индекса:
Остатки.Индексы.Добавить("Номенклатура");
Для Каждого Строка Из Продажи Цикл
СтрокаОстатка = Остатки.Найти(Строка.Номенклатура, "Номенклатура");
Если СтрокаОстатка <> Неопределено Тогда
Строка.Комментарий = "Остаток: " + Формат(СтрокаОстатка.Количество, "ЧГ=0");
КонецЕсли;
КонецЦикла;
Если таблиц несколько и связи сложные, иногда проще и быстрее отдать обе таблицы в запрос и соединить там. Платформа сделает соединение на стороне СУБД или в памяти сервера одним проходом.
Таблица значений как источник запроса и обратная выгрузка
Передача таблицы в запрос
Таблицу передают параметром и сначала помещают во временную таблицу. Прямо в ИЗ основного запроса параметр-таблицу использовать нельзя — только в запросе с ПОМЕСТИТЬ:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.Количество КАК Количество,
| Т.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &Таблица КАК Т
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.Номенклатура КАК Номенклатура,
| СУММА(ВТ.Количество) КАК Количество,
| СУММА(ВТ.Сумма) КАК Сумма
|ИЗ
| ВТ_Данные КАК ВТ
|ГДЕ
| ВТ.Сумма > 10000
|СГРУППИРОВАТЬ ПО
| ВТ.Номенклатура";
Запрос.УстановитьПараметр("Таблица", ТЗ);
Результат = Запрос.Выполнить().Выгрузить();
У каждой колонки, которую выбирают из параметра, должен быть задан тип. Колонка, добавленная без ОписаниеТипов, вызывает ошибку при выполнении запроса. Именно поэтому в первом разделе типы описаны сразу.
Что даёт перенос в запрос:
- 🔗 соединение с регистрами и справочниками (например, подтянуть цены или остатки);
- 🔎 отбор по любым условиям: диапазоны,
ПОДОБНО,В ИЕРАРХИИ; - 📊 группировки и итоги, которые в коде заняли бы десятки строк.
Обратно: результат запроса в таблицу
Выгрузить() превращает результат запроса в таблицу значений с готовыми колонками и типами. Если таблица не нужна, а нужно просто пройти строки, экономнее использовать выборку:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Выборка.Номенклатура, Выборка.Сумма
КонецЦикла;
Выборка не держит в памяти весь результат сразу, а таблица держит. На отчётах в сотни тысяч строк это заметно.
Таблица значений в управляемой форме
На клиенте тонкого клиента
ТаблицаЗначений недоступна. Реквизит формы типа «Таблица значений» на клиенте представлен коллекцией ДанныеФормыКоллекция. Строки в ней читаются похоже, но набор методов другой.
Порядок работы:
- Перейдите на сервер: процедура с директивой
&НаСервере. - Получите полноценную таблицу:
ТЗ = РеквизитФормыВЗначение("Товары");. - Используйте
Найти,Скопировать, индексы, запросы. - Верните результат в форму:
ЗначениеВРеквизитФормы(ТЗ, "Товары");.
На клиенте у коллекции формы есть НайтиСтроки со структурой отбора, перебор Для Каждого и обращение по индексу. Для простой проверки «есть ли строка с таким товаром» серверный вызов не нужен.
Каждый серверный вызов из клиента — это обмен с сервером. В цикле по строкам на клиенте не вызывайте серверную процедуру на каждую строку: соберите всё, что нужно, и обработайте за один вызов.
Частые ошибки при работе со строками таблицы
Удаление строк внутри Для Каждого
Удаление текущей строки в цикле Для Каждого сдвигает коллекцию, и следующая строка пропускается. Надёжных вариантов два.
Через найденные строки:
КУдалению = ТЗ.НайтиСтроки(Новый Структура("Количество", 0));
Для Каждого Строка Из КУдалению Цикл
ТЗ.Удалить(Строка);
КонецЦикла;
Обратным циклом с фиксированной границей:
Всего = ТЗ.Количество();
Для Сч = 1 По Всего Цикл
Строка = ТЗ[Всего - Сч];
Если Строка.Сумма < 0 Тогда
ТЗ.Удалить(Строка);
КонецЕсли;
КонецЦикла;
В обратном цикле удаление строки с конца не меняет индексы ещё не просмотренных строк, поэтому пропусков нет.
Другие ошибки
- ❌ Индекс за границей. Последняя строка —
ТЗ[ТЗ.Количество() - 1], а неТЗ[ТЗ.Количество()]. У пустой таблицы нет дажеТЗ[0]. - ❌ Не проверили результат
Найти. Обращение к полю уНеопределенопрерывает код. Проверяйте<> Неопределенодо чтения. - ❌ Склейка числа со строкой без
Формат."Сумма: " + Строка.Суммавыведет число с пробелами-разделителями. Для логов и ключей используйтеФормат(Число, "ЧГ=0"). - ❌ Изменение строки из
НайтиСтрокис ожиданием копии. Это не копия, а сама строка таблицы. Нужна независимая копия — используйтеСкопировать. - ❌
Свернутьпо колонке без итоговой. Колонки, которых нет ни в группировочных, ни в суммируемых, после свёртки исчезают из таблицы.
Частые вопросы
Как узнать, сколько строк в таблице значений?
ТЗ.Количество(). Он возвращает число строк; у пустой таблицы — 0.Как отсортировать таблицу перед чтением?
ТЗ.Сортировать("Склад, Сумма Убыв"). Направление по умолчанию — по возрастанию, для обратного порядка после имени колонки пишут Убыв.Можно ли найти строку по части строки, например по началу наименования?
Найти и НайтиСтроки сравнивают только на полное равенство. Для поиска по подстроке переберите строки и проверяйте СтрНайти(Строка.Комментарий, "текст") > 0 или передайте таблицу в запрос и используйте ПОДОБНО.Как скопировать только структуру таблицы без строк?
Пустая = ТЗ.СкопироватьКолонки(). Получится таблица с теми же колонками и типами, но без данных.Как посчитать итог по колонке только для части строк?
Скопировать(Отбор) и вызовите у копии Итог("Сумма"). Для одной-двух сумм проще пройти массив из НайтиСтроки и сложить значения в цикле.