Что быстрее в 1С: использование конструкции ВЫБОР КОГДА в запросе или программный обход таблицы значений?

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

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

Разберем подробно, почему классический подход через ВЫБОР КОГДА может работать медленно, и какие альтернативные методы позволят нам добиться мгновенного отклика системы.

Причина низкой производительности конструкции ВЫБОР КОГДА

Проанализируем ситуацию: когда мы передаем Таблицу Значений (ТЗ) в запрос в качестве параметра, платформа создает в СУБД временную таблицу. Если мы используем конструкцию типа СУММА(ВЫБОР КОГДА...), мы сталкиваемся со следующими техническими проблемами:

  1. Отсутствие индексов: По умолчанию временная таблица, созданная из ТЗ, не имеет индексов. При каждом сравнении в операторе ВЫБОР СУБД вынуждена выполнять полное сканирование таблицы (Full Scan). При 9 000 строк и сопоставлении с тысячами записей в регистре количество операций поиска растет в геометрической прогрессии.
  2. Тяжелое сравнение строк: Операция сравнения по наименованию (тип Строка) сама по себе является ресурсоемкой. Если типы данных не совпадают (например, строка неограниченной длины в ТЗ против фиксированной длины в базе), СУБД может игнорировать даже существующие индексы основных таблиц.
  3. Декартово произведение: Если условие ВЫБОР КОГДА прописано неоптимально, SQL-сервер может строить неэффективный план запроса, пытаясь перебрать все возможные комбинации строк.

Рассмотрим пример неоптимального кода, который приводит к зависаниям:


// Неоптимальный подход
"ВЫБРАТЬ
|   СУММА(ВЫБОР КОГДА Регистр.Номенклатура.Наименование = &НаименованиеИзТЗ Тогда 1 ИНАЧЕ 0 КОНЕЦ) КАК Итог
|ИЗ
|   РегистрСведений.ЦеныНоменклатуры КАК Регистр"

Решение №1: Оптимизация запроса через индексацию и соединения

Если мы хотим оставить логику в запросе (что правильно с точки зрения клиент-серверной архитектуры), нам необходимо пересмотреть структуру запроса. Вместо перебора условий ВЫБОР КОГДА, воспользуемся механизмом Левого соединения.

Разберем по шагам правильный алгоритм:

  1. Поместим таблицу значений во временную таблицу.
  2. Обязательно проиндексируем колонку, по которой будет идти поиск.
  3. Выполним соединение временной таблицы с таблицей базы данных.

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


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Передаем ТЗ в запрос
Запрос.Текст = 
"ВЫБРАТЬ
|   ТЗ.Наименование КАК Наименование,
|   ТЗ.ИмяКолонки КАК ИмяКолонки
|ПОМЕСТИТЬ ВТ_ДанныеФормы
|ИЗ
|   &ТЗ КАК ТЗ
|;
|////////////////////////////////////////////////////////////////////////////////
|ИНДЕКСИРОВАТЬ ПО
|   Наименование
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   ВТ.ИмяКолонки,
|   КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Регистр.Номенклатура) КАК Итог
|ИЗ
|   ВТ_ДанныеФормы КАК ВТ
|       ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОстаткиТоваров КАК Регистр
|       ПО ВТ.Наименование = Регистр.Номенклатура.Наименование
|СГРУППИРОВАТЬ ПО
|   ВТ.ИмяКолонки";

Запрос.УстановитьПараметр("ТЗ", ВашаТаблицаЗначений);
Результат = Запрос.Выполнить();

Такой подход перекладывает всю работу на оптимизатор SQL. Наличие индекса во временной таблице позволит серверу БД выполнить Index Seek или Merge Join, что в десятки раз быстрее полного перебора строк.

Решение №2: Использование объекта Соответствие (Map)

Выясним, что делать, если запрос все равно работает недостаточно быстро или логика распределения слишком сложна для языка запросов. В 1С самым быстрым способом поиска в памяти является объект Соответствие.

Важный нюанс: Поиск методом НайтиСтроки() в таблице значений имеет сложность O(N), что при 9 000 строк в цикле даст ужасающую производительность. Поиск в Соответствии работает по принципу хэш-таблицы и выполняется практически мгновенно (сложность O(1)).

Рассмотрим алгоритм реализации:

  1. Создаем объект Соответствие.
  2. Обходим Таблицу Значений один раз, заполняя Ключ (Наименование) и Значение (Имя колонки или номер группы).
  3. Выполняем запрос к базе данных, чтобы получить массив данных для анализа.
  4. Обходим результат запроса в цикле и ищем данные в Соответствии.

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

// 2. Получение данных из БД
Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура.Наименование КАК Наим ИЗ Справочник.Номенклатура");
Выборка = Запрос.Выполнить().Выбрать();

// 3. Быстрый поиск
Пока Выборка.Следующий() Цикл
    Группа = КартаРаспределения.Получить(Выборка.Наим);
    Если Группа <> Неопределено Тогда
        // Выполняем расчет итогов
    КонецЕсли;
КонецЦикла;

Сравнение производительности и рекомендации

Проанализируем ситуацию с передачей данных между клиентом и сервером. В сообщениях форума упоминалось, что 9 000 строк — это «не айс» для передачи. Действительно, при работе с управляемыми формами объект ДанныеФормыКоллекция должен быть преобразован в ТаблицуЗначений на сервере.

Наши рекомендации:

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

← На главную