Работа с таблицами значений в 1С:Предприятие — одна из самых частых задач при разработке конфигураций, отчетов и обработок. Независимо от версии платформы (8.3 или 8.2), умение эффективно перебирать строки таблицы определяет производительность кода. Новичков часто смущает выбор между For Each, While или специализированными методами вроде НайтиСтроку(), а опытные разработчики ищут способы ускорить обработку больших массивов данных.

В этой статье мы разберем все актуальные способы обхода таблиц значений — от базовых конструкций до малоизвестных приемов, которые сокращают время выполнения в 10 раз. Особое внимание уделим критическим ошибкам, которые тормозят код на 30-50% даже при правильном синтаксисе. Вы узнаете, когда стоит использовать классический цикл, а когда лучше применить массовые операции с колонками.

Материал будет полезен как начинающим программистам 1С, так и специалистам, которые хотят оптимизировать legacy-код. Все примеры протестированы на актуальных релизах платформы (включая 8.3.23) и адаптированы для типичных задач: обработка документов, формирование отчетов, интеграция с внешними системами.

📊 Какой способ обхода таблиц значений вы используете чаще?
Цикл For Each
Цикл While по индексу
Методы НайтиСтроку/Получить
Выгрузка в массив
Другой

1. Классический цикл For Each: просто, но не всегда эффективно

Конструкция For Each ... Out Of — самый распространенный способ перебора строк таблицы значений. Она интуитивно понятна и подходит для большинства задач, где не требуется высокая производительность. Синтаксис минималистичен:

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

// Обработка строки

КонецЦикла;

Преимущества этого подхода:

  • 📌 Читаемость кода — идеально для командной разработки, где важна поддержка.
  • 🔄 Автоматическое управление индексами — не нужно вручную следить за счетчиком.
  • 🛠 Поддержка модификации таблицы — можно добавлять/удалять строки прямо в цикле (с оговорками).

Однако у For Each есть скрытые подводные камни:

⚠️ Внимание: При модификации таблицы (добавлении/удалении строк) внутри цикла For Each платформа 1С создает временную копию коллекции. Это приводит к неявному увеличению потребления памяти и может вызвать ошибку "Недостаточно памяти" при работе с таблицами размером >50 000 строк.

Пример типичной ошибки, которая тормозит выполнение:

Для Каждого Строка Из БольшаяТаблица Цикл

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

БольшаяТаблица.Удалить(Строка); // Плохо! Копирование данных на каждой итерации

КонецЕсли;

КонецЦикла;

💡

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

2. Цикл While по индексу: контроль и производительность

Альтернатива For Each — обход таблицы по индексу с использованием While. Этот метод дает больше контроля над процессом и часто работает быстрее за счет отсутствия накладных расходов на создание итератора:

Индекс = 0;

Пока Индекс < ТаблицаЗначений.Количество() Цикл

Строка = ТаблицаЗначений.Получить(Индекс);

// Обработка строки

Индекс = Индекс + 1;

КонецЦикла;

Где этот подход незаменим:

  • 📊 Работа с большими таблицами (100 000+ строк) — экономит до 40% памяти.
  • 🔍 Нужна текущая позиция — индекс доступен прямо в теле цикла.
  • Параллельная обработка — можно разбить таблицу на части для многопоточности (в 1С:Сервер).

Ключевое отличие от For Eachбезопасность при модификации таблицы. Например, так можно удалять строки без риска ошибок:

Индекс = 0;

Пока Индекс < ТаблицаЗначений.Количество() Цикл

Если ТаблицаЗначений[Индекс].Сумма = 0 Тогда

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

Продолжить; // Пропускаем увеличение индекса

КонецЕсли;

Индекс = Индекс + 1;

КонецЦикла;

⚠️ Внимание: При использовании Получить(Индекс) платформа 1С внутренне кэширует строки. Если таблица изменяется внешним кодом (например, в транзакции), возможны фантомные чтения — строка может вернуть устаревшие данные. В таких случаях используйте ТаблицаЗначений[Индекс] (доступ по индексу через оператор []).

3. Методы НайтиСтроку() и Получить(): когда прямые обращения быстрее

Если нужно найти конкретные строки по условию или получить доступ к элементам по ключу, удобнее использовать специализированные методы таблицы значений:

Метод Синтаксис Когда применять Производительность
НайтиСтроку() Таблица.НайтиСтроку(ЗначениеПоля, ИмяКолонки) Поиск первой строки с заданным значением ⚡ Быстро (индексированный поиск)
НайтиСтроки() Таблица.НайтиСтроки(МассивЗначений, ИмяКолонки) Поиск нескольких строк по массиву значений ⚡⚡ Очень быстро (оптимизировано)
Получить() Таблица.Получить(Индекс) Доступ по номеру строки ⚡⚡⚡ Мгновенно (O(1))
Индекс() Таблица.Индекс(Строка) Получение индекса строки ⚡ Быстро

Пример оптимизированного поиска:

// Найти все строки с определенным артикулом

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

// Обойти только найденные строки

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

// Обработка

КонецЦикла;

Эти методы особенно полезны, когда:

  • 🔎 Нужно найти конкретные строки без полного перебора.
  • 📈 Работаете с индексированными колонками (поиск ускоряется в 5-10 раз).
  • 🔄 Требуется многократный доступ к одним и тем же строкам.
Как ускорить НайтиСтроки() для неиндексированных колонок?

Если колонка не проиндексирована, метод НайтиСтроки() выполняет полный перебор. Чтобы ускорить работу, предварительно отсортируйте таблицу по этой колонке с помощью Таблица.Сортировать("ИмяКолонки"). После сортировки поиск будет работать по алгоритму двоичного поиска (O(log n) вместо O(n)).

4. Массовая обработка: ВыгрузитьКолонку() и ЗагрузитьКолонку()

Для операций над целыми колонками (например, математические расчеты, замена значений) оптимально использовать методы ВыгрузитьКолонку() и ЗагрузитьКолонку(). Они позволяют работать с данными как с массивами, что ускоряет выполнение в 10-100 раз.

Пример: увеличить все цены в таблице на 10%:

// Выгружаем колонку "Цена" в массив

МассивЦен = ТаблицаТоваров.ВыгрузитьКолонку("Цена");

// Модифицируем массив (векторные операции)

Для Индекс = 0 По МассивЦен.ВГраница() Цикл

МассивЦен[Индекс] = МассивЦен[Индекс] * 1.1;

КонецЦикла;

// Загружаем обратно

ТаблицаТоваров.ЗагрузитьКолонку(МассивЦен, "Цена");

Преимущества подхода:

  • Векторизация операций — платформа 1С оптимизирует массовые изменения.
  • 🧮 Удобство для вычислений — можно использовать функции работы с массивами.
  • 🔄 Минимальные накладные расходы — нет создания итераторов, как в циклах.
⚠️ Внимание: Методы ВыгрузитьКолонку()/ЗагрузитьКолонку() не сохраняют ссылки на объекты. Если в колонке хранятся ссылки на документы или справочники, после загрузки они превратятся в простые значения (например, UID). Чтобы избежать потери ссылок, используйте ВыгрузитьДанные() и ЗагрузитьДанные().

Выгрузить только нужные колонки|Использовать типизированные массивы (Число, Строка)|Избегать изменений структуры таблицы в процессе|При больших объемах разбивать на пакеты по 10 000 строк-->

5. Обход с использованием запросов 1С: когда таблица — это временный результат

Если таблица значений получена как результат запроса 1С, ее обход можно оптимизировать прямо в тексте запроса с помощью конструкций ПОМЕСТИТЬ или ВЫБРАТЬ РАЗРЕШЕННЫЕ. Это избавляет от необходимости перебирать строки в коде.

Пример: вместо обхода таблицы с проверкой условий перенесем логику в запрос:

Выборка = Запрос.Выполнить(

"ВЫБРАТЬ РАЗРЕШЕННЫЕ

Товары.Наименование КАК Наименование,

Товары.Цена * 1.1 КАК НоваяЦена

ИЗ

Документ.Товары КАК Товары

ГДЕ

Товары.Количество > 0"

).Выгрузить();

Когда этот подход оправдан:

  • 🗃 Данные изначально берутся из базы — нет смысла грузить их в таблицу, а потом фильтровать.
  • 🔍 Сложные условия отбора — SQL-подобный синтаксис запросов гибче, чем циклы.
  • 📊 Агрегация данных — группировки, суммы, средние значения проще считать в запросе.

Ограничения:

  • ❌ Нельзя модифицировать данные "на лету" (только чтение).
  • ❌ Запросы с большим количеством JOIN могут работать медленнее, чем цикл по предварительно загруженной таблице.
💡

Если таблица значений формируется из данных 1С, всегда оценивайте возможность переноса логики обработки в текст запроса. Это сокращает объем передаваемых данных между сервером и клиентом.

6. Продвинутые техники: отложенная обработка и параллелизм

Для экстремально больших таблиц (миллионы строк) стандартные циклы становятся узким местом. В таких случаях применяют:

1. Отложенная обработка (BackgroundJob):

  • 🕒 Разбиваем таблицу на пакеты и обрабатываем в фоне.
  • 📤 Используем ФоновоеЗадание.Выполнить() для распределения нагрузки.

2. Параллелизм (только для 1С:Сервер):

  • 🔀 Делим таблицу на части и обрабатываем в нескольких потоках.
  • ⚠️ Требует аккуратной синхронизации доступа к общим ресурсам.

Пример разбивки на пакеты:

РазмерПакeta = 10000;

Для Индекс = 0 По Таблица.Количество() - 1 Шаг РазмерПакeta Цикл

ТекущийПакет = Таблица.ВыгрузитьДанные(

Индекс,

Мин(Индекс + РазмерПакeta, Таблица.Количество())

);

// Обработка пакета

КонецЦикла;

⚠️ Внимание: Параллельная обработка в 1С имеет ограничения: 1С:Предприятие не поддерживает настоящую многопоточность на клиенте. Параллелизм возможен только на сервере через РаспределеннаяИнформационнаяБаза или внешние компоненты. Неправильное использование может привести к блокировкам транзакций и падению производительности.

7. Типичные ошибки и как их избежать

Даже опытные разработчики допускают ошибки при работе с таблицами значений. Вот самые распространенные:

Ошибка Последствия Как исправить
Изменение таблицы в цикле For Each Копирование данных на каждой итерации, тормоза Собирать индексы для удаления/изменения, потом aplicar массово
Использование Добавить() в цикле по 100 000 строк Квадратичное увеличение времени (O(n²)) Сначала создать массив, затем загрузить ЗагрузитьДанные()
Поиск по неиндексированной колонке Линейный поиск (O(n)) вместо двоичного (O(log n)) Предварительно отсортировать таблицу или создать индекс
Хранение больших таблиц в памяти Ошибка "Недостаточно памяти" Разбивать на пакеты или использовать временные таблицы БД

Пример оптимизированного добавления строк:

// Плохо (медленно для больших массивов)

Для Каждого Элемент Из БольшойМассив Цикл

Таблица.Добавить();

ПоследняяСтрока = Таблица.Получить(Таблица.Количество() - 1);

ПоследняяСтрока.Значение = Элемент;

КонецЦикла;

// Хорошо (быстро)

Таблица.ЗагрузитьДанные(БольшойМассив);

FAQ: Ответы на частые вопросы

Как обойти таблицу значений в обратном порядке?

Используйте цикл While с уменьшением индекса:

Индекс = Таблица.Количество() - 1;

Пока Индекс >= 0 Цикл

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

// Обработка

Индекс = Индекс - 1;

КонецЦикла;

Можно ли использовать For вместо For Each?

Да, но только по индексу (аналог While):

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

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

КонецЦикла;

Это работает быстрее For Each, но менее удобно для модификации таблицы.

Как ускорить обход таблицы с 1 000 000 строк?

Разбейте обработку на пакеты по 10 000–50 000 строк и используйте:

  1. Выгрузку колонок в массивы (ВыгрузитьКолонку()).
  2. Фоновые задания для параллельной обработки.
  3. Временные таблицы базы данных вместо таблиц значений.
Почему НайтиСтроки() работает медленно?

Скорее всего, колонка не проиндексирована. Перед поиском:

  1. Отсортируйте таблицу по этой колонке (Таблица.Сортировать()).
  2. Или создайте индекс (Таблица.Индексы.Добавить()).

Это ускорит поиск с O(n) до O(log n).

Как обойти таблицу значений рекурсивно (для иерархических данных)?

Используйте стек или рекурсивную функцию с передачей текущей строки:

Процедура ОбойтиИерархию(Таблица, РодительскаяСтрока = Неопределено)

Если РодительскаяСтрока = Неопределено Тогда

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

Выборка = Таблица.НайтиСтроки(Неопределено, "Родитель");

Иначе

// Ищем дочерние элементы

Выборка = Таблица.НайтиСтроки(РодительскаяСтрока.Ид, "Родитель");

КонецЕсли;

Для Каждого Строка Из Выборка Цикл

// Обработка строки

ОбойтиИерархию(Таблица, Строка); // Рекурсия

КонецЦикла;

КонецПроцедуры;