Чтение, поиск и отбор строк в ТаблицеЗначений: приёмы для кода 1С 8.3

· 9 мин

Содержание

Значение из таблицы значений в 1С получают через строку и колонку: ТЗ[0].Сумма вернёт сумму из первой строки, ТЗ.Найти(Ссылка, "Номенклатура") — первую строку с нужным товаром, а ТЗ.ВыгрузитьКолонку("Сумма") — все значения колонки массивом. Нумерация строк начинается с нуля.

Остальное — вопрос задачи: перебрать всё, найти одну запись, отобрать группу строк или отдать таблицу в запрос. Ниже каждый способ с рабочим кодом для платформы 8.3. Объект ТаблицаЗначений доступен на сервере, в толстом клиенте и во внешнем соединении. В тонком клиенте и веб-клиенте его нет — это важно для кода управляемых форм.

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

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

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

Строку добавляют методом Добавить(). Он возвращает объект СтрокаТаблицыЗначений, в который пишут значения по именам колонок:

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

Ещё три способа наполнения, которые сокращают код:

  • 📥 ТЗ.Вставить(Индекс) — вставляет пустую строку в заданную позицию, а не в конец.
  • 📦 ТЗ.ЗагрузитьКолонку(Массив, "Количество") — заливает колонку из массива. Строк должно быть не меньше, чем элементов массива.
  • 🔁 ЗаполнитьЗначенияСвойств(НоваяСтрока, Источник) — переносит одноимённые поля из структуры, строки другой таблицы или выборки запроса.

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

Перебор строк и доступ к значениям колонок

Цикл Для Каждого

Самый читаемый вариант. Подходит, когда нужно пройти все строки и что-то посчитать или прочитать:

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

Для простой суммы цикл не нужен: ТЗ.Итог("Сумма") вернёт то же самое одной строкой.

Обращение по индексу

Когда нужен номер строки, сосед сверху или снизу, используют индекс от 0 до Количество() - 1:

Для Индекс = 0 По ТЗ.Количество() - 1 Цикл
    Строка = ТЗ[Индекс];
    Если Индекс > 0 И Строка.Номенклатура = ТЗ[Индекс - 1].Номенклатура Тогда
        Строка.Комментарий = "Повтор товара";
    КонецЕсли;
КонецЦикла;

ТЗ[Индекс] и ТЗ.Получить(Индекс) дают одно и то же. Обратная задача — узнать номер строки — решается методом ТЗ.Индекс(Строка).

Три способа прочитать ячейку

ЗаписьКогда удобна
Строка.Суммаимя колонки известно при написании кода
Строка["Сумма"]имя колонки хранится в переменной или приходит параметром
Строка[3]обход колонок по порядку, например при выгрузке в файл

Универсальный обход всех ячеек строки выглядит так:

Для Каждого Колонка Из ТЗ.Колонки Цикл
    Значение = Строка[Колонка.Имя];
    Сообщить(Колонка.Имя + ": " + Строка(Значение));
КонецЦикла;

Если колонка может отсутствовать, проверьте её заранее: ТЗ.Колонки.Найти("Сумма") вернёт Неопределено, когда такой колонки нет. Обращение к несуществующей колонке через точку прерывает выполнение ошибкой.

Все значения колонки сразу

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

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

Выборка строк по условию: НайтиСтроки и Скопировать

У таблицы значений нет метода «Выборка» в том смысле, в каком он есть у результата запроса (Результат.Выбрать()). Отбор группы строк делают двумя методами.

НайтиСтроки — массив ссылок на строки

Метод принимает структуру, где ключи — имена колонок, а значения — то, что ищем. Условия объединяются через «И», сравнение только на равенство:

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

Для Каждого Строка Из НайденныеСтроки Цикл
    Строка.Комментарий = "Проверено";
КонецЦикла;

Результат — массив. Если ничего не нашлось, массив пустой, а не Неопределено. Элементы массива — те же строки исходной таблицы: изменение Строка.Комментарий меняет данные в ТЗ.

Скопировать с отбором — новая таблица

Когда нужна отдельная таблица с частью строк и колонок, используйте Скопировать:

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

Первый параметр может быть и массивом строк, например результатом НайтиСтроки. Второй — список нужных колонок через запятую. Без параметров метод копирует всю таблицу.

Условия «больше», «меньше», «содержит»

Структура отбора понимает только равенство. Для диапазонов есть два пути:

  1. Небольшая таблица — перебор циклом и сбор подходящих строк в массив.
  2. Большая таблица или сложное условие — передача таблицы в запрос (раздел ниже) и отбор через ГДЕ.
КрупныеСтроки = Новый Массив;
Для Каждого Строка Из ТЗ Цикл
    Если Строка.Сумма > 10000 Тогда
        КрупныеСтроки.Добавить(Строка);
    КонецЕсли;
КонецЦикла;
КрупныеПродажи = ТЗ.Скопировать(КрупныеСтроки);

Поиск одной записи методом Найти

Найти возвращает первую строку, где встретилось значение, или Неопределено:

Строка = ТЗ.Найти(Товар, "Номенклатура");
Если Строка <> Неопределено Тогда
    Цена = Строка.Сумма / Строка.Количество;
КонецЕсли;

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

Чем Найти отличается от НайтиСтроки:

МетодЧто ищетЧто возвращаетПусто
Найти(Значение, Колонки)одно значение в одной или нескольких колонкахпервую строкуНеопределено
НайтиСтроки(Структура)сочетание значений в разных колонкахмассив строкпустой массив

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

Типы значений должны совпадать. Число 5 и строка "5" — разные значения, поиск по колонке со строками не найдёт число. Для дат учитывается и время: '20260107' не равно '20260107120000'.

Индексы: ускорение поиска в больших таблицах

Без индекса Найти и НайтиСтроки просматривают таблицу от начала до конца. На сотне строк разницы не видно. На десятках тысяч строк, когда поиск вызывается внутри цикла, время растёт квадратично: 50 000 строк × 50 000 поисков — это уже минуты.

Индекс добавляют один раз, до серии поисков:

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

Правила, которые стоит держать в голове:

  • 🎯 Индекс помогает, когда набор колонок в отборе совпадает с набором колонок индекса. Индекс по «Склад, Номенклатура» рассчитан на отбор по обеим колонкам сразу.
  • 🐢 Каждый индекс замедляет добавление и изменение строк и занимает память. Не создавайте индекс «на всякий случай».
  • 🧹 Индекс живёт, пока жива таблица. После Скопировать его нужно создавать заново.
  • 📏 На маленьких таблицах (до нескольких сотен строк) индекс почти ничего не даёт.

Типичный сценарий — сопоставление двух таблиц. Вместо поиска в цикле по второй таблице без индекса:

Остатки.Индексы.Добавить("Номенклатура");
Для Каждого Строка Из Продажи Цикл
    СтрокаОстатка = Остатки.Найти(Строка.Номенклатура, "Номенклатура");
    Если СтрокаОстатка <> Неопределено Тогда
        Строка.Комментарий = "Остаток: " + Формат(СтрокаОстатка.Количество, "ЧГ=0");
    КонецЕсли;
КонецЦикла;

Если таблиц несколько и связи сложные, иногда проще и быстрее отдать обе таблицы в запрос и соединить там. Платформа сделает соединение на стороне СУБД или в памяти сервера одним проходом.

Таблица значений как источник запроса и обратная выгрузка

Передача таблицы в запрос

Таблицу передают параметром и сначала помещают во временную таблицу. Прямо в ИЗ основного запроса параметр-таблицу использовать нельзя — только в запросе с ПОМЕСТИТЬ:

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|    Т.Номенклатура КАК Номенклатура,
|    Т.Количество КАК Количество,
|    Т.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
|    &Таблица КАК Т
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|    ВТ.Номенклатура КАК Номенклатура,
|    СУММА(ВТ.Количество) КАК Количество,
|    СУММА(ВТ.Сумма) КАК Сумма
|ИЗ
|    ВТ_Данные КАК ВТ
|ГДЕ
|    ВТ.Сумма > 10000
|СГРУППИРОВАТЬ ПО
|    ВТ.Номенклатура";
Запрос.УстановитьПараметр("Таблица", ТЗ);
Результат = Запрос.Выполнить().Выгрузить();

У каждой колонки, которую выбирают из параметра, должен быть задан тип. Колонка, добавленная без ОписаниеТипов, вызывает ошибку при выполнении запроса. Именно поэтому в первом разделе типы описаны сразу.

Что даёт перенос в запрос:

  • 🔗 соединение с регистрами и справочниками (например, подтянуть цены или остатки);
  • 🔎 отбор по любым условиям: диапазоны, ПОДОБНО, В ИЕРАРХИИ;
  • 📊 группировки и итоги, которые в коде заняли бы десятки строк.

Обратно: результат запроса в таблицу

Выгрузить() превращает результат запроса в таблицу значений с готовыми колонками и типами. Если таблица не нужна, а нужно просто пройти строки, экономнее использовать выборку:

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    // Выборка.Номенклатура, Выборка.Сумма
КонецЦикла;

Выборка не держит в памяти весь результат сразу, а таблица держит. На отчётах в сотни тысяч строк это заметно.

Таблица значений в управляемой форме

На клиенте тонкого клиента ТаблицаЗначений недоступна. Реквизит формы типа «Таблица значений» на клиенте представлен коллекцией ДанныеФормыКоллекция. Строки в ней читаются похоже, но набор методов другой.

Порядок работы:

  1. Перейдите на сервер: процедура с директивой &НаСервере.
  2. Получите полноценную таблицу: ТЗ = РеквизитФормыВЗначение("Товары");.
  3. Используйте Найти, Скопировать, индексы, запросы.
  4. Верните результат в форму: ЗначениеВРеквизитФормы(ТЗ, "Товары");.

На клиенте у коллекции формы есть НайтиСтроки со структурой отбора, перебор Для Каждого и обращение по индексу. Для простой проверки «есть ли строка с таким товаром» серверный вызов не нужен.

Каждый серверный вызов из клиента — это обмен с сервером. В цикле по строкам на клиенте не вызывайте серверную процедуру на каждую строку: соберите всё, что нужно, и обработайте за один вызов.

Частые ошибки при работе со строками таблицы

Удаление строк внутри Для Каждого

Удаление текущей строки в цикле Для Каждого сдвигает коллекцию, и следующая строка пропускается. Надёжных вариантов два.

Через найденные строки:

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

Обратным циклом с фиксированной границей:

Всего = ТЗ.Количество();
Для Сч = 1 По Всего Цикл
    Строка = ТЗ[Всего - Сч];
    Если Строка.Сумма < 0 Тогда
        ТЗ.Удалить(Строка);
    КонецЕсли;
КонецЦикла;

В обратном цикле удаление строки с конца не меняет индексы ещё не просмотренных строк, поэтому пропусков нет.

Другие ошибки

  • ❌ Индекс за границей. Последняя строка — ТЗ[ТЗ.Количество() - 1], а не ТЗ[ТЗ.Количество()]. У пустой таблицы нет даже ТЗ[0].
  • ❌ Не проверили результат Найти. Обращение к полю у Неопределено прерывает код. Проверяйте <> Неопределено до чтения.
  • ❌ Склейка числа со строкой без Формат. "Сумма: " + Строка.Сумма выведет число с пробелами-разделителями. Для логов и ключей используйте Формат(Число, "ЧГ=0").
  • ❌ Изменение строки из НайтиСтроки с ожиданием копии. Это не копия, а сама строка таблицы. Нужна независимая копия — используйте Скопировать.
  • ❌ Свернуть по колонке без итоговой. Колонки, которых нет ни в группировочных, ни в суммируемых, после свёртки исчезают из таблицы.
Проверьте себя
Что вернёт ТЗ.НайтиСтроки(Отбор), если подходящих строк нет?
Как получить последнюю строку таблицы ТЗ?
Что нужно сделать перед передачей таблицы значений параметром в запрос?

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

Как узнать, сколько строк в таблице значений?
Методом ТЗ.Количество(). Он возвращает число строк; у пустой таблицы — 0.
Как отсортировать таблицу перед чтением?
Методом ТЗ.Сортировать("Склад, Сумма Убыв"). Направление по умолчанию — по возрастанию, для обратного порядка после имени колонки пишут Убыв.
Можно ли найти строку по части строки, например по началу наименования?
Найти и НайтиСтроки сравнивают только на полное равенство. Для поиска по подстроке переберите строки и проверяйте СтрНайти(Строка.Комментарий, "текст") > 0 или передайте таблицу в запрос и используйте ПОДОБНО.
Как скопировать только структуру таблицы без строк?
Пустая = ТЗ.СкопироватьКолонки(). Получится таблица с теми же колонками и типами, но без данных.
Как посчитать итог по колонке только для части строк?
Отберите строки через Скопировать(Отбор) и вызовите у копии Итог("Сумма"). Для одной-двух сумм проще пройти массив из НайтиСтроки и сложить значения в цикле.