Разработка и Конфигурация · 10 мин
Как перебрать строки таблицы значений в 1С
Способы перебора строк таблицы значений в 1С: цикл Для каждого, цикл по индексу, поиск и отбор строк, типичные ошибки и примеры кода на встроенном языке.
Таблица значений — один из самых используемых объектов встроенного языка 1С. В ней хранятся результаты запросов, данные табличных частей, промежуточные расчёты. Перебор строк нужен практически в любом алгоритме: для суммирования, поиска, модификации данных или выгрузки в другие структуры.
В 1С существует несколько способов обхода строк: цикл Для каждого ... Цикл, цикл со счётчиком по индексу, методы НайтиСтроки() и Найти() для выборочного перебора. У каждого подхода есть свои условия применения, ограничения и подводные камни — например, нельзя удалять строки внутри прямого цикла без риска пропустить элементы.
Ниже разобраны основные приёмы с рабочими примерами кода, которые можно переносить в свои процедуры почти без изменений.
Что такое таблица значений и когда нужен перебор
Таблица значений — несохраняемый объект в памяти, состоящий из колонок и строк. Создать её можно программно (Новый ТаблицаЗначений), получить как результат запроса (Запрос.Выполнить().Выгрузить()) или выгрузить из табличной части документа.
Перебор строк требуется в типовых задачах: подсчёт итогов с условием, поиск дублей, заполнение одной таблицы по данным другой, проверка корректности данных перед записью документа, формирование печатных форм и отчётов.
Базовый перебор: цикл «Для каждого»
Самый читаемый и безопасный способ — цикл Для каждого. На каждой итерации переменная цикла получает ссылку на текущую строку, и все поля доступны через точку.
Для каждого СтрокаТЗ Из ТаблицаЗначений Цикл
СуммаИтог = СуммаИтог + СтрокаТЗ.Сумма;
Если СтрокаТЗ.Количество = 0 Тогда
Сообщить("Нулевое количество в строке: " + СтрокаТЗ.Номенклатура);
КонецЕсли;
КонецЦикла;
Важно понимать: переменная цикла — это ссылка на строку, а не копия. Изменяя СтрокаТЗ.Сумма внутри цикла, вы изменяете данные самой таблицы. Это удобно для массовых пересчётов, но может стать источником ошибок, если таблица используется где-то ещё.
Цикл Для каждого подходит для чтения и изменения данных, но не для удаления строк — об этом подробнее ниже.
Перебор по индексу: когда нужен номер строки
Если внутри цикла нужен порядковый номер строки или требуется удалять элементы, используйте цикл со счётчиком. Индексация строк начинается с нуля, поэтому последняя строка имеет индекс Количество() - 1.
КоличествоСтрок = ТаблицаЗначений.Количество();
Для Инд = 0 По КоличествоСтрок - 1 Цикл
СтрокаТЗ = ТаблицаЗначений[Инд];
СтрокаТЗ.НомерПоПорядку = Инд + 1;
КонецЦикла;
Удаление строк в обратном порядке
Классическая ошибка — удалять строки в прямом цикле. После удаления индексы сдвигаются, и следующая строка пропускается. Правильный приём — идти от конца к началу:
Для Инд = -ТаблицаЗначений.Количество() + 1 По 0 Цикл
РеальныйИндекс = -Инд;
Если ТаблицаЗначений[РеальныйИндекс].Пометка Тогда
ТаблицаЗначений.Удалить(РеальныйИндекс);
КонецЕсли;
КонецЦикла;
Альтернатива — собрать строки на удаление в отдельный массив внутри обычного цикла Для каждого, а затем удалить их вторым проходом. Этот вариант читается проще и реже приводит к ошибкам с индексами.
Выборочный перебор: НайтиСтроки и Найти
Когда нужны не все строки, а только удовлетворяющие условию, эффективнее метод НайтиСтроки(). Он принимает структуру отбора и возвращает массив строк.
Отбор = Новый Структура("Склад, Отгружен", СкладОсновной, Ложь);
НайденныеСтроки = ТаблицаЗначений.НайтиСтроки(Отбор);
Для каждого СтрокаТЗ Из НайденныеСтроки Цикл
СтрокаТЗ.Отгружен = Истина;
КонецЦикла;
Если нужна только одна строка (или достаточно первой найденной), используйте Найти(Значение, ИмяКолонки). Метод вернёт строку или Неопределено — обязательно проверяйте результат перед обращением к полям, иначе получите ошибку времени выполнения.
СтрокаТовара = ТаблицаЗначений.Найти(ТекущийТовар, "Номенклатура");
Если СтрокаТовара <> Неопределено Тогда
Цена = СтрокаТовара.Цена;
КонецЕсли;
Методы поиска работают только на точное равенство. Для условий «больше», «содержит», «в списке» придётся перебирать строки вручную с проверкой внутри цикла или использовать запрос с временной таблицей.
Производительность: что выбрать для больших таблиц
На небольших таблицах (сотни строк) разница между способами незаметна. На десятках тысяч строк выбор метода влияет на время выполнения заметно.
Основные рекомендации:
- Цикл
Для каждого— базовый вариант, быстрый и безопасный для чтения и изменения. - Агрегатные задачи (сумма, количество) решайте методами
Итог(),Свернуть()или запросом — не перебором. - Многократный поиск по таблице внутри цикла — антипаттерн. Вместо этого создайте соответствие (
Соответствие) с ключом по нужной колонке. - Сложные фильтрации и соединения таблиц переносите в запрос — он выполняется на уровне СУБД и работает на порядки быстрее.
Сравнение способов перебора
Универсальный обход всех строк для чтения и изменения. Не подходит для удаления строк внутри цикла.
Нужен номер строки или удаление элементов. Требует аккуратности с границами и направлением обхода.
Выборка по точному равенству. Не работает с условиями «больше», «содержит», «в списке».
Сложные отборы, соединения, группировки на больших данных. Требует помещения таблицы во временную таблицу.
Практический пример: сводная задача
Типовая ситуация: есть таблица товаров, нужно просуммировать количество по каждой номенклатуре и отметить позиции с нулевым остатком. Решение через соответствие:
ИтогиПоТоварам = Новый Соответствие;
Для каждого СтрокаТЗ Из ТаблицаТоваров Цикл
ТекущееКоличество = ИтогиПоТоварам[СтрокаТЗ.Номенклатура];
Если ТекущееКоличество = Неопределено Тогда
ТекущееКоличество = 0;
КонецЕсли;
ИтогиПоТоварам.Вставить(СтрокаТЗ.Номенклатура, ТекущееКоличество + СтрокаТЗ.Количество);
КонецЦикла;
Для каждого СтрокаТЗ Из ТаблицаТоваров Цикл
Если ИтогиПоТоварам[СтрокаТЗ.Номенклатура] = 0 Тогда
СтрокаТЗ.ТребуетЗаказа = Истина;
КонецЕсли;
КонецЦикла;
Обратите внимание: таблица перебирается дважды, но оба прохода линейные. Вложенный поиск дал бы квадратичную сложность и на больших данных работал бы ощутимо медленнее.
- 1Определите задачу: чтение, изменение, удаление или поиск строк
- 2Выберите подходящий цикл или метод поиска
- 3Проверьте, что таблица заполнена: ТаблицаЗначений.Количество() > 0
- 4При удалении строк используйте обратный обход или промежуточный массив
- 5Проверьте результат на тестовых данных, включая пустую таблицу
Проверка результата и отладка
Перед перебором убедитесь, что таблица не пуста — иначе цикл просто не выполнится, и логика «молча» не сработает. Простая проверка Если ТаблицаЗначений.Количество() = 0 Тогда избавит от трудноуловимых ситуаций, когда алгоритм вроде бы отработал, но ничего не сделал.
Для отладки удобно выводить содержимое строки через Сообщить() или смотреть таблицу в конфигураторе через точку останова и «Вычислить выражение». Убедитесь, что типы данных в колонках соответствуют ожиданиям: например, колонка «Сумма» может содержать Неопределено, и сложение выдаст ошибку.
Как проверить результат
Совпадает с ТаблицаЗначений.Количество()
Меньше ожидаемого — возможно, строки удалялись в цикле
Значения в колонках обновились после цикла
Данные прежние — изменялась копия, а не строка таблицы
Возвращена строка, поля доступны
Неопределено — обращение к полям вызовет ошибку
Частые вопросы
Как перебрать строки табличной части документа?
Точно так же, как таблицу значений: цикл «Для каждого СтрокаТЧ Из Документ.Товары Цикл». Табличная часть поддерживает те же методы — Найти(), НайтиСтроки(), Итог(), индексацию. Дополнительно у строк есть свойство НомерСтроки, начинающееся с единицы.
Можно ли прервать перебор досрочно?
Да. Оператор «Прервать» выходит из цикла полностью, «Продолжить» переходит к следующей итерации. Если строка найдена и дальнейший обход не нужен, используйте «Прервать» — это сэкономит время на больших таблицах.
Чем перебор отличается от метода Свернуть()?
Свернуть() группирует строки по указанным колонкам и суммирует числовые поля, изменяя саму таблицу. Перебор сохраняет все строки и позволяет реализовать любую логику. Если задача — просто сгруппировать и просуммировать, Свернуть() короче и быстрее.
Как перебрать строки с сортировкой?
Сначала отсортируйте таблицу методом Сортировать("Дата Возр, Сумма Убыв"), затем выполняйте обычный цикл. Порядок обхода будет соответствовать сортировке. Сам цикл порядок строк не меняет.
Почему в цикле возникает ошибка «Поле объекта не обнаружено»?
Обращение идёт к колонке, которой нет в таблице. Проверьте состав колонок через ТаблицаЗначений.Колонки или в отладчике. Частая причина — опечатка в имени или то, что таблица получена из другого запроса, чем ожидалось.
Что быстрее: перебор или запрос?
Для простого обхода разницы практически нет. Для фильтрации, соединений и агрегации на больших объёмах запрос заметно быстрее, так как выполняется на стороне СУБД. Перебор оправдан для построчной логики, которую сложно выразить в запросе.
Что важно запомнить
Для обычного обхода используйте «Для каждого», для работы с индексами и удаления — цикл со счётчиком в обратном порядке, для выборки по равенству — НайтиСтроки(). Проверяйте пустую таблицу и результат Найти(), а сложные отборы на больших данных выносите в запрос.