Как эффективно хранить и проверять наличие пар значений в 1С

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

В процессе разработки алгоритмов часто возникает задача: необходимо накапливать множество пар элементов (например, связок «Номенклатура — Характеристика», «Узел А — Узел Б», связей в графах или соответствий объектов) и периодически проверять, существует ли конкретная пара в коллекции. Обычные массивы и списки значений для этой задачи подходят плохо, так как линейный перебор O(N) на больших объемах данных приводит к существенным просадкам производительности.

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

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

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

Рассмотрим пример создания и поиска по составному индексу:


// Создаем таблицу значений и описываем колонки
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("ЭлементА");
ТЗ.Колонки.Добавить("ЭлементБ");

// Добавляем составной индекс по двум колонкам
ТЗ.Колонки.Индексы.Добавить("ЭлементА, ЭлементБ");

// Заполняем данными
СтрокаТЗ = ТЗ.Добавить();
СтрокаТЗ.ЭлементА = Ссылка1;
СтрокаТЗ.ЭлементБ = "Параметр1";

// Проверка наличия пары
Отбор = Новый Структура;
Отбор.Вставить("ЭлементА", ИскомыйЭлементА);
Отбор.Вставить("ЭлементБ", ИскомыйЭлементБ);

НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
    // Пара существует
КонецЕсли;

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

  1. Метод НайтиСтроки() задействует составной индекс только в том случае, если в структуре отбора переданы все поля индекса. Если передать только одно поле, платформа выполнит полный перебор строк.
  2. Объект ТаблицаЗначений доступен только на стороне сервера или в толстом клиенте. В тонком клиенте использовать его нельзя.
  3. Если связь является ненаправленной (то есть пара «А–Б» эквивалентна паре «Б–А»), перед помещением в таблицу и перед поиском значения следует нормализовать, упорядочив их. Обратите внимание: сравнивать ссылочные типы операторами < или > напрямую нельзя. Для ссылок сравнение выполняют по строковому представлению идентификатора через XMLСтрока(Ссылка).

Способ 2. Вложенные соответствия (соответствие соответствий)

Коллекция Соответствие работает на основе хэш-таблицы в оперативной памяти, что обеспечивает поиск со скоростью O(1). Для хранения пар можно использовать двухуровневую структуру: внешнее соответствие хранит первый элемент пары в качестве ключа, а в качестве значения — вложенное соответствие со вторыми элементами.

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


// Инициализация структуры
Пары = Новый Соответствие;

// Добавление новой пары
Вложенное = Пары[ЭлементА];
Если Вложенное = Неопределено Тогда
    Вложенное = Новый Соответствие;
    Пары[ЭлементА] = Вложенное;
КонецЕсли;
Вложенное[ЭлементБ] = Истина; // Или вес/дополнительное значение связи

// Проверка наличия пары
Если Пары[ИскомыйА] <> Неопределено И Пары[ИскомыйА][ИскомыйБ] <> Неопределено Тогда
    // Пара существует в коллекции
КонецЕсли;

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

  1. Скорость: Поиск выполняется мгновенно и работает одинаково быстро как на сервере, так и в контексте тонкого клиента.
  2. Удаление и дубликаты: Коллекция автоматически защищена от дубликатов ключей, а удаление отдельной пары выполняется методом Удалить() без необходимости сдвига массива строк, как в таблицах.
  3. Расход памяти: На больших объемах (сотни тысяч элементов) создание множества мелких вложенных объектов Соответствие может создавать ощутимые накладные расходы по памяти по сравнению с плоскими структурами.

Способ 3. Одно соответствие с составным строковым ключом или хэшем

Если для алгоритма важна только проверка факта существования пары «А–Б» и не требуется получать список всех «Б» для конкретного «А», оптимально использовать одно плоское Соответствие, сформировав уникальный строковый ключ.

Если среди значений присутствуют ссылочные типы, нельзя использовать простое приведение Строка(Ссылка), так как наименования могут совпадать или изменяться. Надежным идентификатором ссылки является вызов XMLСтрока(Ссылка).

Разберем пример реализации:


// Функция формирования уникального ключа пары
Функция ПолучитьКлючПары(ЗначениеА, ЗначениеБ)
    
    СтрА = ?(ЗначениеЗаполнено(ЗначениеА) И ТипЗнч(ЗначениеА) = Тип("СправочникСсылка.Номенклатура"), 
              XMLСтрока(ЗначениеА), Строка(ЗначениеА));
    СтрБ = ?(ЗначениеЗаполнено(ЗначениеБ) И ТипЗнч(ЗначениеБ) = Тип("СправочникСсылка.ХарактеристикиНоменклатуры"), 
              XMLСтрока(ЗначениеБ), Строка(ЗначениеБ));
              
    Возврат СтрА + "#" + СтрБ;
    
КонецФункции

// Использование
МножествоПар = Новый Соответствие;

// Добавление
Ключ = ПолучитьКлючПары(ТоварСсылка, ХарактеристикаСсылка);
МножествоПар.Вставить(Ключ, Истина);

// Проверка
Если МножествоПар[ПолучитьКлючПары(ПроверяемыйТовар, ПроверяемаяХарактеристика)] <> Неопределено Тогда
    // Пара найдена
КонецЕсли;

Если строки получаются слишком длинными и требуется оптимизировать расход памяти, для получения ключа можно использовать встроенный объект ХешированиеДанных (например, с алгоритмом ХешФункция.MD5 или ХешФункция.CRC32).

Способ 4. Пакетная проверка через запрос и временные таблицы

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

Выясним, как организовать пакетную проверку:

  1. Выгружаем массив пар в типизированную ТаблицаЗначений.
  2. Передаем таблицу в Запрос как параметр.
  3. Во временной таблице обязательно указываем директиву ИНДЕКСИРОВАТЬ ПО для полей связки.
  4. Выполняем сопоставление через ВНУТРЕННЕЕ СОЕДИНЕНИЕ или условие ГДЕ (Поле1, Поле2) В (ВЫБРАТЬ ...).

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

← На главную