К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как перебрать строки таблицы значений в 1С

Способы перебора строк таблицы значений в 1С: цикл Для каждого, цикл по индексу, поиск и отбор строк, типичные ошибки и примеры кода на встроенном языке.

Таблица значений — один из самых используемых объектов встроенного языка 1С. В ней хранятся результаты запросов, данные табличных частей, промежуточные расчёты. Перебор строк нужен практически в любом алгоритме: для суммирования, поиска, модификации данных или выгрузки в другие структуры.

В 1С существует несколько способов обхода строк: цикл Для каждого ... Цикл, цикл со счётчиком по индексу, методы НайтиСтроки() и Найти() для выборочного перебора. У каждого подхода есть свои условия применения, ограничения и подводные камни — например, нельзя удалять строки внутри прямого цикла без риска пропустить элементы.

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

Что такое таблица значений и когда нужен перебор

Таблица значений — несохраняемый объект в памяти, состоящий из колонок и строк. Создать её можно программно (Новый ТаблицаЗначений), получить как результат запроса (Запрос.Выполнить().Выгрузить()) или выгрузить из табличной части документа.

Перебор строк требуется в типовых задачах: подсчёт итогов с условием, поиск дублей, заполнение одной таблицы по данным другой, проверка корректности данных перед записью документа, формирование печатных форм и отчётов.

Базовый перебор: цикл «Для каждого»

Самый читаемый и безопасный способ — цикл Для каждого. На каждой итерации переменная цикла получает ссылку на текущую строку, и все поля доступны через точку.

Для каждого СтрокаТЗ Из ТаблицаЗначений Цикл

СуммаИтог = СуммаИтог + СтрокаТЗ.Сумма;

Если СтрокаТЗ.Количество = 0 Тогда

Сообщить("Нулевое количество в строке: " + СтрокаТЗ.Номенклатура);

КонецЕсли;

КонецЦикла;

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

Цикл Для каждого подходит для чтения и изменения данных, но не для удаления строк — об этом подробнее ниже.

Перебор по индексу: когда нужен номер строки

Если внутри цикла нужен порядковый номер строки или требуется удалять элементы, используйте цикл со счётчиком. Индексация строк начинается с нуля, поэтому последняя строка имеет индекс Количество() - 1.

КоличествоСтрок = ТаблицаЗначений.Количество();

Для Инд = 0 По КоличествоСтрок - 1 Цикл

СтрокаТЗ = ТаблицаЗначений[Инд];

СтрокаТЗ.НомерПоПорядку = Инд + 1;

КонецЦикла;

Удаление строк в обратном порядке

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

Для Инд = -ТаблицаЗначений.Количество() + 1 По 0 Цикл

РеальныйИндекс = -Инд;

Если ТаблицаЗначений[РеальныйИндекс].Пометка Тогда

ТаблицаЗначений.Удалить(РеальныйИндекс);

КонецЕсли;

КонецЦикла;

Альтернатива — собрать строки на удаление в отдельный массив внутри обычного цикла Для каждого, а затем удалить их вторым проходом. Этот вариант читается проще и реже приводит к ошибкам с индексами.

Типичные ошибки
01
Удаление строк в прямом цикле «Для каждого» — часть строк пропускается из-за сдвига индексов
02
Обращение к несуществующей колонке — возникает ошибка «Поле объекта не обнаружено», проверяйте состав колонок
03
Сравнение значений разных типов без приведения — число и строка «0» не равны, условие не сработает
04
Использование перебора там, где достаточно Итог() или Свернуть() — лишний код и потеря производительности

Выборочный перебор: НайтиСтроки и Найти

Когда нужны не все строки, а только удовлетворяющие условию, эффективнее метод НайтиСтроки(). Он принимает структуру отбора и возвращает массив строк.

Отбор = Новый Структура("Склад, Отгружен", СкладОсновной, Ложь);

НайденныеСтроки = ТаблицаЗначений.НайтиСтроки(Отбор);

Для каждого СтрокаТЗ Из НайденныеСтроки Цикл

СтрокаТЗ.Отгружен = Истина;

КонецЦикла;

Если нужна только одна строка (или достаточно первой найденной), используйте Найти(Значение, ИмяКолонки). Метод вернёт строку или Неопределено — обязательно проверяйте результат перед обращением к полям, иначе получите ошибку времени выполнения.

СтрокаТовара = ТаблицаЗначений.Найти(ТекущийТовар, "Номенклатура");

Если СтрокаТовара <> Неопределено Тогда

Цена = СтрокаТовара.Цена;

КонецЕсли;

Методы поиска работают только на точное равенство. Для условий «больше», «содержит», «в списке» придётся перебирать строки вручную с проверкой внутри цикла или использовать запрос с временной таблицей.

Производительность: что выбрать для больших таблиц

На небольших таблицах (сотни строк) разница между способами незаметна. На десятках тысяч строк выбор метода влияет на время выполнения заметно.

Основные рекомендации:

  • Цикл Для каждого — базовый вариант, быстрый и безопасный для чтения и изменения.
  • Агрегатные задачи (сумма, количество) решайте методами Итог(), Свернуть() или запросом — не перебором.
  • Многократный поиск по таблице внутри цикла — антипаттерн. Вместо этого создайте соответствие (Соответствие) с ключом по нужной колонке.
  • Сложные фильтрации и соединения таблиц переносите в запрос — он выполняется на уровне СУБД и работает на порядки быстрее.

Сравнение способов перебора

Для каждого

Универсальный обход всех строк для чтения и изменения. Не подходит для удаления строк внутри цикла.

Цикл по индексу

Нужен номер строки или удаление элементов. Требует аккуратности с границами и направлением обхода.

НайтиСтроки / Найти

Выборка по точному равенству. Не работает с условиями «больше», «содержит», «в списке».

Запрос

Сложные отборы, соединения, группировки на больших данных. Требует помещения таблицы во временную таблицу.

Практический пример: сводная задача

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

ИтогиПоТоварам = Новый Соответствие;

Для каждого СтрокаТЗ Из ТаблицаТоваров Цикл

ТекущееКоличество = ИтогиПоТоварам[СтрокаТЗ.Номенклатура];

Если ТекущееКоличество = Неопределено Тогда

ТекущееКоличество = 0;

КонецЕсли;

ИтогиПоТоварам.Вставить(СтрокаТЗ.Номенклатура, ТекущееКоличество + СтрокаТЗ.Количество);

КонецЦикла;

Для каждого СтрокаТЗ Из ТаблицаТоваров Цикл

Если ИтогиПоТоварам[СтрокаТЗ.Номенклатура] = 0 Тогда

СтрокаТЗ.ТребуетЗаказа = Истина;

КонецЕсли;

КонецЦикла;

Обратите внимание: таблица перебирается дважды, но оба прохода линейные. Вложенный поиск дал бы квадратичную сложность и на больших данных работал бы ощутимо медленнее.

Порядок действий при переборе
  1. 1
    Определите задачу: чтение, изменение, удаление или поиск строк
  2. 2
    Выберите подходящий цикл или метод поиска
  3. 3
    Проверьте, что таблица заполнена: ТаблицаЗначений.Количество() > 0
  4. 4
    При удалении строк используйте обратный обход или промежуточный массив
  5. 5
    Проверьте результат на тестовых данных, включая пустую таблицу

Проверка результата и отладка

Перед перебором убедитесь, что таблица не пуста — иначе цикл просто не выполнится, и логика «молча» не сработает. Простая проверка Если ТаблицаЗначений.Количество() = 0 Тогда избавит от трудноуловимых ситуаций, когда алгоритм вроде бы отработал, но ничего не сделал.

Для отладки удобно выводить содержимое строки через Сообщить() или смотреть таблицу в конфигураторе через точку останова и «Вычислить выражение». Убедитесь, что типы данных в колонках соответствуют ожиданиям: например, колонка «Сумма» может содержать Неопределено, и сложение выдаст ошибку.

Контрольные точки

Как проверить результат

Количество обработанных строк
Норма

Совпадает с ТаблицаЗначений.Количество()

Тревожный признак

Меньше ожидаемого — возможно, строки удалялись в цикле

Изменённые данные
Норма

Значения в колонках обновились после цикла

Тревожный признак

Данные прежние — изменялась копия, а не строка таблицы

Результат Найти()
Норма

Возвращена строка, поля доступны

Тревожный признак

Неопределено — обращение к полям вызовет ошибку

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

Как перебрать строки табличной части документа?

Точно так же, как таблицу значений: цикл «Для каждого СтрокаТЧ Из Документ.Товары Цикл». Табличная часть поддерживает те же методы — Найти(), НайтиСтроки(), Итог(), индексацию. Дополнительно у строк есть свойство НомерСтроки, начинающееся с единицы.

Можно ли прервать перебор досрочно?

Да. Оператор «Прервать» выходит из цикла полностью, «Продолжить» переходит к следующей итерации. Если строка найдена и дальнейший обход не нужен, используйте «Прервать» — это сэкономит время на больших таблицах.

Чем перебор отличается от метода Свернуть()?

Свернуть() группирует строки по указанным колонкам и суммирует числовые поля, изменяя саму таблицу. Перебор сохраняет все строки и позволяет реализовать любую логику. Если задача — просто сгруппировать и просуммировать, Свернуть() короче и быстрее.

Как перебрать строки с сортировкой?

Сначала отсортируйте таблицу методом Сортировать("Дата Возр, Сумма Убыв"), затем выполняйте обычный цикл. Порядок обхода будет соответствовать сортировке. Сам цикл порядок строк не меняет.

Почему в цикле возникает ошибка «Поле объекта не обнаружено»?

Обращение идёт к колонке, которой нет в таблице. Проверьте состав колонок через ТаблицаЗначений.Колонки или в отладчике. Частая причина — опечатка в имени или то, что таблица получена из другого запроса, чем ожидалось.

Что быстрее: перебор или запрос?

Для простого обхода разницы практически нет. Для фильтрации, соединений и агрегации на больших объёмах запрос заметно быстрее, так как выполняется на стороне СУБД. Перебор оправдан для построчной логики, которую сложно выразить в запросе.

Итог

Что важно запомнить

Для обычного обхода используйте «Для каждого», для работы с индексами и удаления — цикл со счётчиком в обратном порядке, для выборки по равенству — НайтиСтроки(). Проверяйте пустую таблицу и результат Найти(), а сложные отборы на больших данных выносите в запрос.