При разработке сложных отчетов или обработок проведения документов в 1С мы часто сталкиваемся с необходимостью фильтровать данные по большому списку условий. Распространенной ошибкой является попытка формирования длинного текста запроса, содержащего сотни условий ИЛИ, каждое из которых требует передачи отдельного параметра. Давайте разберемся, почему возникает ошибка «Превышение допустимой вложенности операций» и как решить эту задачу правильно.
Ошибка «Превышение допустимой вложенности операций» возникает из-за ограничений СУБД (например, MS SQL Server) на сложность и размер текста SQL-запроса, а также на количество передаваемых параметров. В платформе 1С существует внутренний лимит на количество параметров в одном запросе (обычно он составляет около 500-508 штук). Когда вы используете цикл для добавления условий типа &Параметр1, &Параметр2 и так далее, вы очень быстро упираетесь в этот «потолок».
Попытки «обмануть» систему, например, разбивая запрос на части или выполняя запросы в цикле, являются неэффективными. Каждый вызов Выполнить() внутри цикла — это дополнительный сетевой запрос к серверу БД, что многократно замедляет работу программы.
Вместо того чтобы плодить сотни параметров, рассмотрим архитектурно верный подход — передачу набора данных через временную таблицу. Это стандарт индустрии, который позволяет работать с любым количеством записей без риска получения ошибок переполнения.
Сначала создадим ТаблицаЗначений, в которую соберем все необходимые нам данные. В отличие от параметров, таблица значений передается в запрос одним объектом, что снимает ограничения на количество строк.
ТаблицаСделок = Новый ТаблицаЗначений;
ТаблицаСделок.Колонки.Добавить("Сделка", Новый ОписаниеТипов("ДокументСсылка.ЗаказПокупателя"));
// Добавляем остальные колонки по аналогии...
Для каждого СтрокаПлатежа Из ТаблицаПлатежей Цикл
Строка = ТаблицаСделок.Добавить();
Строка.Сделка = СтрокаПлатежа.Сделка;
// Заполняем остальные поля...
КонецЦикла;
Теперь передадим нашу таблицу в запрос как параметр и поместим ее во временную таблицу базы данных. Это позволит серверу БД эффективно соединить наш список с основными данными.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТаблицаДанных КАК ТаблицаДанных;
|
|ВЫБРАТЬ
| Регистр.СуммаОстаток
|ИЗ
| РегистрНакопления.Взаиморасчеты.Остатки КАК Регистр
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Данные КАК ВТ
| ПО Регистр.Сделка = ВТ.Сделка";
Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаСделок);
Результат = Запрос.Выполнить().Выгрузить();
Рассмотрим, почему такой подход является оптимальным для разработки:
ИЛИ, так как оптимизатор запросов может эффективно использовать индексы.Проанализировав ситуацию, мы рекомендуем полностью отказаться от динамического формирования текста запроса в циклах. Если вы столкнулись с подобной ошибкой, первым делом проверьте, нельзя ли заменить фильтрацию по параметрам на ЛЕВОЕ СОЕДИНЕНИЕ с временной таблицей. Если требуется блокировка строк для проведения документа, не забывайте про конструкцию ДЛЯ ИЗМЕНЕНИЯ при работе с временными таблицами, но делайте это аккуратно, чтобы избежать взаимных блокировок при одновременной работе нескольких пользователей.
Используя этот метод, вы избавитесь от ошибок, упростите поддержку кода и значительно ускорите выполнение тяжелых операций в 1С.