Как скопировать строки из одной таблицы значений в другую с отбором и сортировкой?

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

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

Метод 1: Использование функции НайтиСтроки()

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

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


ОтборСтрок = Новый Структура("Номенклатура", Справочники.Номенклатура.ПустаяСсылка());
Для каждого СтрТЗ1 из ТЗ_1 Цикл
    // Устанавливаем значение для поиска из текущей строки первой таблицы
    ОтборСтрок.Номенклатура = СтрТЗ1.Номенклатура;
    
    // Получаем массив строк из второй таблицы
    НайденныеСтроки = ТЗ_2.НайтиСтроки(ОтборСтрок);
    
    Для каждого СтрТЗ2 из НайденныеСтроки Цикл
        // Обработка найденных данных
    КонецЦикла;
КонецЦикла;

Важный нюанс: Метод НайтиСтроки() возвращает не новые объекты, а массив ссылок (указателей) на строки оригинальной таблицы ТЗ_2. Если вы измените данные в СтрТЗ2, они изменятся и в исходной таблице ТЗ_2. Однако у массива нет метода Сортировать(), поэтому, если вам нужен строгий порядок вывода отобранных строк, этот метод потребует дополнительных манипуляций.

Оптимизация поиска через индексацию

Проанализируем ситуацию с производительностью. Если в ТЗ_2 содержатся тысячи строк, поиск перебором при каждой итерации цикла по ТЗ_1 будет крайне медленным. Чтобы ускорить процесс, необходимо использовать индексы.

Перед началом цикла добавим индекс по колонке поиска:

ТЗ_2.Индексы.Добавить("Номенклатура");

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

Метод 2: Копирование части таблицы с сортировкой

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

Рассмотрим реализацию этого подхода:


Отбор = Новый Структура("Номенклатура");
Для каждого СтрТЗ1 из ТЗ_1 Цикл
    Отбор.Номенклатура = СтрТЗ1.Номенклатура;
    
    // Создаем новую физическую таблицу из отобранных строк
    КопияТЗ_2 = ТЗ_2.Скопировать(Отбор);
    
    // Теперь нам доступна полноценная сортировка
    КопияТЗ_2.Сортировать("Дата Возр, Цена Убыв");
    
    Для каждого СтрТЗ2 из КопияТЗ_2 Цикл
        // Работаем с отсортированными данными
    КонецЦикла;
КонецЦикла;

Выясним причину, по которой этот метод стоит использовать с осторожностью. На каждой итерации цикла Для каждого создается новый объект таблицы значений в оперативной памяти. Если ТЗ_1 содержит тысячи строк, это приведет к избыточному потреблению ресурсов и фрагментации памяти. Мы советуем использовать этот способ только в тех случаях, когда объем данных в выборке невелик.

Метод 3: Использование запроса к таблицам значений

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

Разберем по шагам, как это сделать:

  1. Помещаем обе таблицы значений в менеджер временных таблиц.
  2. В тексте запроса выполняем ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ по ключевому полю (в нашем случае — "Номенклатура").
  3. Используем конструкцию УПОРЯДОЧИТЬ ПО для получения нужной сортировки.
  4. Выгружаем результат обратно в таблицу или обходим выборку.

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

Метод 4: Альтернатива через объект Соответствие (Map)

Если связь между таблицами — "один-ко-многим" и нам нужна максимальная скорость без использования запросов, мы можем применить объект Соответствие.

Проанализируем алгоритм:


КартаСтрок = Новый Соответствие;
Для каждого Стр из ТЗ_2 Цикл
    Ключ = Стр.Номенклатура;
    МассивОбъектов = КартаСтрок.Получить(Ключ);
    Если МассивОбъектов = Неопределено Тогда
        МассивОбъектов = Новый Массив;
        КартаСтрок.Вставить(Ключ, МассивОбъектов);
    КонецЕсли;
    МассивОбъектов.Добавить(Стр);
КонецЦикла;

// Теперь в основном цикле поиск будет мгновенным
Для каждого СтрТЗ1 из ТЗ_1 Цикл
    НайденныеДанные = КартаСтрок.Получить(СтрТЗ1.Номенклатура);
    Если НайденныеДанные <> Неопределено Тогда
        // Обработка массива
    КонецЕсли;
КонецЦикла;

Поиск в Соответствии работает через хеш-таблицы, что зачастую быстрее, чем поиск по индексированной таблице значений, особенно при огромных объемах данных.

Использование Построителя Запроса и СКД

В некоторых случаях, когда фильтры и сортировки задаются динамически (например, пользователем в интерфейсе), удобно использовать ПостроительЗапроса. Ему можно назначить таблицу значений в качестве источника данных: Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ_2). Это дает гибкость в управлении отборами без ручного написания сложных условий.

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

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

← На главную