Найти в Дзене
1с разное

Запрос в цикле

Всем привет! Есть определенный класс задач, в решении которых используется запрос в цикле. Я рассказываю о своем опыте, на истину в последней инстанции не претендую. За 20 лет я сталкивался с задачами, в которых запрос в цикле это необходимая мера, и с ситуациями, в которых запрос в цикле это вынужденная мера, пусть и крадущая время выполнения кода. В целом, я придерживаюсь мнения, что новичку нужно в процессе обучения, нарабатывания опыта и прогрессирования - исходить из той мысли, что использовать запрос в цикле "не комильфо". Со временем программист научится различать ситуации и сценарии, когда нельзя использовать запрос в цикле, когда это критично, а когда это не критично. Данная статья родилась в процессе обсуждений статьи Алгоритмы: улучшаем загрузку из эксель. Например, в задаче получения себестоимости (остатка, последней цены и т.д.) товаров из документа. Идти по циклу по табличной части документа, в каждой итерации обращаться через запрос к регистрам для получения себест
Оглавление

Всем привет!

Есть определенный класс задач, в решении которых используется запрос в цикле. Я рассказываю о своем опыте, на истину в последней инстанции не претендую.

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

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

Со временем программист научится различать ситуации и сценарии, когда нельзя использовать запрос в цикле, когда это критично, а когда это не критично.

Данная статья родилась в процессе обсуждений статьи Алгоритмы: улучшаем загрузку из эксель.

Класс задач, где запрещен запрос в цикле

Проведение документов

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

На курсе по программированию преподаватель мне сказал, что это неправильно, что это замедляет работу алгоритма. Как это проверить, тогда я не знал. Поэтому зарубил себе на носу, что за это снижают балл на экзамене.

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

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

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

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

Если же вы считаете себестоимость (остатки, последнюю цену и т.д.) не в процедуре проведения, а в своей внешней обработке для вывода в таблицу для дальнейшего анализа. И строк товаров в документе у вас немного, то смело можете прописывать запрос в цикле. Никто вам за это не снизит балл. Блокировок у вас не будет, иногда вы даже не заметите разницу во времени исполнения кода.

Вывод динамического списка

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

Для определения места в коде, которое выполняется больше всего времени, вам потребуется использовать режим отладки и замер производительности.

Разработчики платформы давно знали об этой особенности, и решили этот вопрос - предоставив нам, внедренцам и разработчикам приложений 1С, использовать метод ПриПолученииДанных().

Со временем я добавлю к статье алгоритмы для демонстрации.

А расширить класс задач, в которых запрос в цикле запрещено использовать, вы сможете самостоятельно. Исходя из примеров выше.

Класс задач, где запрос в цикле - необходимая мера.

Поиск номенклатуры с подходящими свойствами и характеристиками

Функция ПолучитьСписокХарактеристик()

ПустойМассив = Новый Массив;
Если ОтборПоСвойствам.Количество() = 0 Тогда
Возврат ПустойМассив;
КонецЕсли;

ТЗ = ОтборПоСвойствам.Скопировать(,);
ТЗ.Свернуть("Свойство");

СписокХарактеристик = Новый Массив;
Для Каждого СтрТЗ Из ТЗ Цикл

ПараметрыПоиска = Новый Структура;
ПараметрыПоиска.Вставить("Свойство", СтрТЗ.Свойство);

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

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

Запрос.УстановитьПараметр("Свойство", СтрТЗ.Свойство);
Запрос.УстановитьПараметр("СписокЗначенийСвойств", СписокЗначенийСвойств);

Запрос.УстановитьПараметр("ИспользоватьСписокХарактеристик", ?(СписокХарактеристик.Количество() = 0, Ложь, Истина));
Запрос.УстановитьПараметр("СписокХарактеристик", СписокХарактеристик);

Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат ПустойМассив;
КонецЕсли;

СписокХарактеристик = Результат.Выгрузить().ВыгрузитьКолонку("Объект");

КонецЦикла;

Возврат СписокХарактеристик;

КонецФункции

Вот этот алгоритм использован в работе Подбор номенклатуры по характеристикам - там суть такая - вы ставите одну галочку напротив свойства - определенный цвет/определенный размер и т.д., список номенклатуры уменьшается с каждым выбранным свойством.

Обратите внимание , в алгоритме используется запрос в цикле. Это другой класс задач.

Задача следующая. Выбираем номенклатуру по "Цвету": Красный или Белый или Зеленый, по "Материалу": либо пластик, либо пластик+. Сколько уже вариантов появилось? 6?

Нет, их 1000 - тысяча характеристик, потому что есть еще 20 других свойств с 100 разными значениями. И с каждым новым добавлением свойства и значений, список номенклатуры, которая подходит по характеристикам, сужается.

Поиск договоров с подходящими диапазонами наценок

Функция ПолучитьКоличествоДоговоров(Цепочка)

МассивСтрок = СтрРазделить(Цепочка,",",Ложь);
Если МассивСтрок.Количество()=0 Тогда
Возврат 0;
КонецЕсли;

СписокДоговоров = Новый Массив;
Для Каждого НомерСтроки Из МассивСтрок Цикл

Мин = ИсходнаяТаблица.Получить(НомерСтроки-1).Мин;
Макс = ИсходнаяТаблица.Получить(НомерСтроки-1).Макс;
Процент = ИсходнаяТаблица.Получить(НомерСтроки-1).Процент;

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

Запрос.УстановитьПараметр("Мин" , Мин);
Запрос.УстановитьПараметр("Макс" , Макс);
Запрос.УстановитьПараметр("Процент" , Процент);
Запрос.УстановитьПараметр("ТекДата" , ТекущаяДата());
Запрос.УстановитьПараметр("ИспользоватьСписокДоговоров", ?(СписокДоговоров.Количество() = 0, Ложь, Истина));
Запрос.УстановитьПараметр("СписокДоговоров", СписокДоговоров);

Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат 0 ;
КонецЕсли;

СписокДоговоров = Результат.Выгрузить().ВыгрузитьКолонку("ДоговорПоставки");

КонецЦикла;

Возврат СписокДоговоров.Количество();

КонецФункции

С каждой итерацией круг поиска подходящих договоров сужается...

Класс задач, где запрос в цикле вынужденная мера

Связанные списки или структуры подчиненности

Для построения дерева нужно обратиться к СУБД для каждого родительского документа в поисках подчиненных - в целом работа с деревьями это всегда циклы и рекурсия.

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

Групповые обработки документов

Запрос в цикле используется при групповом удалении документов - Удаление документов для любых баз на управляемых формах.

На этом все. Всем добра!

С пользой для клиентов, Рустем