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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При загрузке и обработке больших массивов информации во внешней обработке или серверном модуле разработчики часто сталкиваются с задачей: в объекте ТаблицаЗначений на сотни тысяч строк присутствует колонка с текстовыми наименованиями (например, «Номенклатура», «Производитель» или «Валюта»). Требуется получить список уникальных значений, найти соответствующие элементы в базе данных 1С и заменить строки на найденные ссылки без потери производительности.

Разберем различные подходы к решению этой задачи: от стандартных методов платформы до высокопроизводительного сопоставления через хэш-таблицы.

Вариант 1. Метод «Свернуть» для копии колонки

Проанализируем классический способ платформы 1С. Метод Свернуть() позволяет быстро схлопнуть дублирующиеся записи. Чтобы не модифицировать исходную большую таблицу, выгрузим целевую колонку в отдельную промежуточную таблицу значений.

Рассмотрим пример кода:


// Создаем копию только нужной колонки
ТЗУникальных = ИсходнаяТЗ.Скопировать(, "ПроизводительНаименование");

// Сворачиваем по группировочной колонке
ТЗУникальных.Свернуть("ПроизводительНаименование");

// Теперь ТЗУникальных содержит только уникальные строки

Особенности метода:

  1. Метод прост в реализации и выполняется на уровне ядра платформы.
  2. Если исходная таблица содержит более 500 000 строк, операция полного копирования даже одной колонки и последующая свертка создают заметную нагрузку на оперативную память рабочего процесса rphost.
  3. После нахождения ссылок для уникальных значений вам все равно потребуется дополнительный цикл для обратной подстановки ссылок в исходную таблицу.

Вариант 2. Использование объекта «Соответствие» (Хэш-таблица) — оптимальный способ

Выясним, почему коллекция Соответствие является самым эффективным инструментом в сценариях нормализации данных. Объект Соответствие в платформе 1С реализован как хэш-таблица. Это дает ключевые преимущества:

  1. Автоматическая уникальность ключей: метод Вставить(Ключ, Значение) перезаписывает значение при совпадении ключа, не создавая дубликатов.
  2. Скорость доступа $O(1)$: поиск элемента по ключу примитивного типа (или ссылочного типа) происходит практически мгновенно благодаря расчету хэш-кода, в отличие от линейного перебора массива или вызова НайтиСтроки().

Посмотрим на практическую реализацию двухэтапного сопоставления:


// Шаг 1. Сбор уникальных значений в момент обхода или чтения
КэшПроизводителей = Новый Соответствие;

Для Каждого СтрТЗ Из ИсходнаяТЗ Цикл
    // Ключом выступает текст, значением изначально может быть Неопределено
    КэшПроизводителей.Вставить(СтрТЗ.ПроизводительСтрока, Неопределено);
КонецЦикла;

// Шаг 2. Заполнение ссылок для уникальных ключей
Для Каждого ЭлементКэша Из КэшПроизводителей Цикл
    ТекстПоиска = ЭлементКэша.Ключ;
    СсылкаНайдена = Справочники.Производители.НайтиПоНаименованию(ТекстПоиска, Истина);
    
    Если ЗначениеЗаполнено(СсылкаНайдена) Тогда
        КэшПроизводителей.Вставить(ТекстПоиска, СсылкаНайдена);
    Иначе
        // Обработка отсутствующих элементов (создание или оповещение)
        КэшПроизводителей.Вставить(ТекстПоиска, Справочники.Производители.ПустаяСсылка());
    КонецЕсли;
КонецЦикла;

// Шаг 3. Быстрая замена строк на ссылки за один проход
Для Каждого СтрТЗ Из ИсходнаяТЗ Цикл
    СтрТЗ.ПроизводительСсылка = КэшПроизводителей[СтрТЗ.ПроизводительСтрока];
КонецЦикла;

Рассмотрим подробнее: на шаге 3 мы делаем ровно один проход по исходной таблице, обращаясь к соответствию по индексу КэшПроизводителей[Ключ]. Для 650 000 строк такой цикл отрабатывает за доли секунды, полностью исключая тяжелые поисковые операции.

Вариант 3. Выборка через «ПостроительЗапроса» без создания временных таблиц в СУБД

Разберем еще один способ получения уникального списка — использование объекта ПостроительЗапроса. Он умеет принимать в качестве источника данных объект ТаблицаЗначений напрямую через ОписаниеИсточникаДанных:


Построитель = Новый ПостроительЗапроса;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ИсходнаяТЗ);
Построитель.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   ИсходныеДанные.ПроизводительСтрока КАК ПроизводительСтрока
    |ИЗ
    |   ИсходныеДанные КАК ИсходныеДанные";

Построитель.Выполнить();
ТЗУникальных = Построитель.Результат.Выгрузить();

Важное ограничение: если передавать таблицу значений напрямую в параметры обычного объекта Запрос (Запрос.УстановитьПараметр("ТЗ", ИсходнаяТЗ)), платформа будет сериализовать данные и создавать временную таблицу в базе данных tempdb на стороне СУБД. При сотнях тысяч строк это приводит к дополнительным затратам времени на передачу и запись данных по сети.

Оптимизация массовой замены значений: «ЗагрузитьКолонку»

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


МассивСсылок = Новый Массив;

Для Каждого Стр Из ИсходнаяТЗ Цикл
    МассивСсылок.Добавить(КэшПроизводителей[Стр.ПроизводительСтрока]);
КонецЦикла;

// Загружаем готовый массив в колонку
ИсходнаяТЗ.ЗагрузитьКолонку(МассивСсылок, "ПроизводительСсылка");

Проверка пустых значений для колонок со составными типами

При обработке колонок, содержащих пустые ссылки различных справочников, необходимо корректно проверять их заполненность:

  1. Функция ЗначениеЗаполнено(Значение) вернет Ложь как для пустой ссылки любого типа (СправочникСсылка.Номенклатура.Пустая()), так и для Неопределено или пустой строки.
  2. Если тип гарантированно ссылочный, можно вызвать метод ссылки Значение.Пустая().

Рекомендации по работе с большими объемами данных (от 500 000 строк)

  1. Избегайте метода НайтиСтроки() в циклах: даже при наличии индекса метод НайтиСтроки() при каждом вызове создает в памяти новый Массив. Сотни тысяч вызовов вызывают резкую деградацию производительности и нагружают сборщик мусора.
  2. Потоковое чтение: если данные поступают из текстового файла, JSON или CSV, не загружайте весь файл в промежуточную ТаблицаЗначений. Читайте файл построчно через ЧтениеТекста или ЧтениеXML, сразу накапливая соответствие уникальных записей.
  3. Использование Временного Хранилища: при передаче больших массивов между клиентом и сервером используйте методы ПоместитьВоВременноеХранилище() и ПолучитьИзВременногоХранилища() по уникальному идентификатору формы, чтобы не гонять тяжелый контекст формы при серверных вызовах.
← На главную