Работа с таблицами значений в 1С:Предприятие — одна из самых частых задач при разработке конфигураций, отчетов и обработок. Независимо от версии платформы (8.3 или 8.2), умение эффективно перебирать строки таблицы определяет производительность кода. Новичков часто смущает выбор между For Each, While или специализированными методами вроде НайтиСтроку(), а опытные разработчики ищут способы ускорить обработку больших массивов данных.
В этой статье мы разберем все актуальные способы обхода таблиц значений — от базовых конструкций до малоизвестных приемов, которые сокращают время выполнения в 10 раз. Особое внимание уделим критическим ошибкам, которые тормозят код на 30-50% даже при правильном синтаксисе. Вы узнаете, когда стоит использовать классический цикл, а когда лучше применить массовые операции с колонками.
Материал будет полезен как начинающим программистам 1С, так и специалистам, которые хотят оптимизировать legacy-код. Все примеры протестированы на актуальных релизах платформы (включая 8.3.23) и адаптированы для типичных задач: обработка документов, формирование отчетов, интеграция с внешними системами.
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 строк и используйте:
- Выгрузку колонок в массивы (
ВыгрузитьКолонку()). - Фоновые задания для параллельной обработки.
- Временные таблицы базы данных вместо таблиц значений.
Почему НайтиСтроки() работает медленно?
Скорее всего, колонка не проиндексирована. Перед поиском:
- Отсортируйте таблицу по этой колонке (
Таблица.Сортировать()). - Или создайте индекс (
Таблица.Индексы.Добавить()).
Это ускорит поиск с O(n) до O(log n).
Как обойти таблицу значений рекурсивно (для иерархических данных)?
Используйте стек или рекурсивную функцию с передачей текущей строки:
Процедура ОбойтиИерархию(Таблица, РодительскаяСтрока = Неопределено)
Если РодительскаяСтрока = Неопределено Тогда
// Начинаем с корневых элементов
Выборка = Таблица.НайтиСтроки(Неопределено, "Родитель");
Иначе
// Ищем дочерние элементы
Выборка = Таблица.НайтиСтроки(РодительскаяСтрока.Ид, "Родитель");
КонецЕсли;
Для Каждого Строка Из Выборка Цикл
// Обработка строки
ОбойтиИерархию(Таблица, Строка); // Рекурсия
КонецЦикла;
КонецПроцедуры;