При работе с большими объемами данных в объекте ТаблицаЗначений часто возникает необходимость быстрого поиска или проверки уникальности строк. В платформе 1С предусмотрен механизм индексирования колонок таблицы, однако разработчики нередко задаются вопросом: в какой именно момент создавать индекс и как платформа обновляет данные индекса при динамическом добавлении новых строк.
Рассмотрим подробно механику работы индексов, выясним оптимальный момент для их создания и разберем сопутствующие нюансы и альтернативные решения.
Разберем, как платформа 1С обрабатывает коллекцию Индексы объекта ТаблицаЗначений:
ТЗ.Индексы.Добавить("Колонка_1") вызван сразу после инициализации структуры пустой таблицы, платформа сразу создает пустую структуру индекса. При каждом последующем вызове методов Добавить(), Вставить() или при изменении значений в ячейках платформа автоматически обновляет внутренний индекс в оперативной памяти. Дополнительно перезапускать переиндексацию не требуется.Добавить() коллекции индексов выполняет полное построение индекса по всем существующим строкам, после чего также продолжает поддерживать его в актуальном состоянии.Добавить() и потребление оперативной памяти, так как платформе требуется поддерживать внутреннее дерево поиска. Однако при наличии алгоритмов частого поиска строк выигрыш во времени выполнения перекрывает эти микрозатраты в десятки и сотни раз.Проанализируем практический пример. Если в процессе первичного наполнения таблицы требуется проверять уникальность строк через метод Найти(), а затем повторно дозаполнять данные, индекс необходимо объявлять в самом начале — до начала циклов заполнения.
Посмотрим на пример правильной организации кода:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Колонка_1", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Колонка_2", Новый ОписаниеТипов("Строка"));
// Создаем индекс сразу после объявления колонок
ТЗ.Индексы.Добавить("Колонка_1");
// Алгоритм 1: Заполнение с проверкой уникальности
Для Каждого ЭлементДанных Из ИсточникДанных Цикл
// Поиск выполняется быстро благодаря активному индексу
НайденнаяСтрока = ТЗ.Найти(ЭлементДанных.Код, "Колонка_1");
Если НайденнаяСтрока = Неопределено Тогда
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Колонка_1 = ЭлементДанных.Код;
НоваяСтрока.Колонка_2 = ЭлементДанных.Наименование;
КонецЕсли;
КонецЦикла;
// Алгоритм 2: Дозаполнение данных
Для Каждого ДопЭлемент Из ДополнительныйИсточник Цикл
НайденнаяСтрока = ТЗ.Найти(ДопЭлемент.Код, "Колонка_1");
Если НайденнаяСтрока <> Неопределено Тогда
НайденнаяСтрока.Колонка_2 = ДопЭлемент.Наименование;
КонецЕсли;
КонецЦикла;
Обратим внимание на важные технические особенности работы методов поиска в 1С:
Найти(): Индекс используется платформой только в том случае, если поиск выполняется по одной конкретной колонке. Если вторым параметром передать список колонок через запятую (например, "Колонка_1, Колонка_2"), поиск будет выполняться последовательным перебором без использования индексов.НайтиСтроки(): Данный метод принимает объект Структура. Чтобы поиск задействовал составной индекс (например, "Колонка1, Колонка2"), параметры структуры отбора должны точно соответствовать составу индекса или его левому префиксу.ТЗ.Сортировать(...) выполняет лишь физическую перестановку строк в памяти, но не строит структуру поиска. Вызовы Найти() и НайтиСтроки() на отсортированной, но неиндексированной таблице продолжают работать медленно (полным сканированием).Булево) неэффективно.Разберем ситуации, когда вместо индексирования ТаблицаЗначений эффективнее применить другие инструменты:
Соответствие: Хэш-таблица Соответствие осуществляет поиск по ключу за константное время. Если требуется только проверять уникальность по одному ключу и быстро получать ссылку на строку таблицы, связка ТаблицаЗначений + Соответствие работает еще быстрее:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Код");
ТЗ.Колонки.Добавить("Наименование");
ХэшТаблица = Новый Соответствие;
Для Каждого Элемент Из ИсточникДанных Цикл
НайденнаяСтрока = ХэшТаблица.Получить(Элемент.Код);
Если НайденнаяСтрока = Неопределено Тогда
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Код = Элемент.Код;
НоваяСтрока.Наименование = Элемент.Наименование;
ХэшТаблица.Вставить(Элемент.Код, НоваяСтрока);
КонецЕсли;
КонецЦикла;
Свернуть(): Если во время заполнения таблицы нет необходимости сразу обращаться к найденным строкам, наиболее производительно просто заполнить всю таблицу строк без промежуточных проверок, а в конце выполнить метод ТЗ.Свернуть("Колонка_1", ...).Запрос, выгрузить во временную таблицу с директивой ИНДЕКСИРОВАТЬ ПО и выполнить соединение или группировку средствами СУБД.