Запрос в цикле 1с что это
Правильное использование Циклов в языке 1С может помочь решить поставленные задачи и упростить алгоритмы. Неправильный подход к циклам усложняет программный код и алгоритм, делает его менее «прозрачным» и ясным для других специалистов, уменьшает скорость работы программного кода. Иногда некорректное использование в шутку называют «противозаконным», хотя это и не смешно (особенно это касается всем известных Запросов в циклах). Хоть и считается что Запрос в цикле — это грубейшая ошибка, но бывают моменты, когда иначе просто не сделать.
В данной статье не будет примера, так как подобные ситуации встречаются достаточно редко и зависят от контекста использования, скорости работы алгоритма, оптимальности алгоритма, других возможных факторов.
1. 3 вида циклов
В данной статье будет рассмотрен не столько синтаксис, сколько разумное использование циклов и применение для упрощения кода и его визуальной оптимизации.
Всего существует 3 вида циклов: «Для», «Для каждого», «Пока». О каждом из них можно прочитать в Синтакс-помощнике.
«Для» - циклическое повторение операторов внутри «Цикл -КонецЦикла», где можно использовать итератор с указываемыми границами (шаг всегда равен единице). При этом границы можно регулировать внутри самого цикла. Главное не забывать, что границы должны быть типа Число.
«Для каждого» - обход коллекции значений. Каждая итерация – последующий элемент коллекции, обход будет до тех пор, пока элементы в коллекции не закончатся. Каждый элемент коллекции — это либо элемент, либо строка с элементами. Например, для Массива – это Элемент, для Таблицы значений – это Строка (обращение к элементу в строке будет через точку).
«Пока» - циклическое повторение, пока выполняется условие в выражении. Например, «Пока Итератор > 0 Цикл». Для исключения рекурсии рекомендуется после создания цикла сразу вписать изменение выражения, например, «Итератор = Итератор – 1;».
Также для работы циклов существуют два оператора: «Прервать» и «Продолжить»:
1. «Прервать» - используется для выхода из цикла на любом моменте выполнения.
2. «Продолжить» - используется для перехода в начало цикла пропуская все, что находится после оператора «Продолжить».
2. Примеры использования циклов
Теперь можно рассмотреть несколько примеров с использованием разных циклов. Примеры только показывают, как можно использовать циклы, и не претендуют на единственно верный вариант решения.
Задача: необходимо перебрать табличную часть 1С и удалить дублирующие строки, оставив только первые встречаемые элементы справочника Номенклатура.
Данную задачу можно решить циклом «Для каждого». При использовании «Для каждого» обращение будет к элементам коллекции – Строкам.
Теперь определимся с методом решения. Следует помнить, что, удаляя строки из таблицы все последующие смещаются на количество удаленных строк. А это значит, что, используя «Для каждого», необходимо сохранить номер строки или индекса, чтобы после прохождения всей коллекции удалить необходимые строки в другом уже цикле. Первую строку мы просто сохраняем как проверяемое значение, так как первая строка всегда уникальна. Именно здесь можно использовать оператор «Продолжить», для пропуска первой строки на всякие проверки.
Так как нам необходимо удалять элементы с конца, то необходима сортировка по убыванию. А сортировка возможна в таблицах типа СписокЗначений, ТаблицаЗначений и прочие (помним о том, что Массив не сортируется). Значит для реализации задачи необходимо объявить переменную с одним из вышеуказанных типов. Рассмотрим:
УдаляемыеСтроки=НовыйТаблицаЗначений;
УдаляемыеСтроки.Колонки.Добавить("Индекс");
ДлякаждогоСтрокаИзТабличнаяЧастьЦикл
ЕслиСтрока.НомерСтроки= 1 Тогда
ПроверяемоеЗначение=Строка.Номенклатура;
ЕслиСтрока.Номенклатура=ПроверяемоеЗначениеТогда
ИндексСтроки=ТабличнаяЧасть.Индекс(Строка);
УдаляемаяСтрока=УдаляемыеСтроки.Добавить();
УдаляемаяСтрока.Индекс=ИндексСтроки;
ПроверяемоеЗначение=Строка.Номенклатура;
УдаляемыеСтроки.Сортировать("Индекс Убыв");
ДлякаждогоСтрокаИзУдаляемыеСтрокиЦикл
ТабличнаяЧасть.Удалить(Строка.Индекс);
Для удаления элементов с конца мы заполняем, как указано в примере Таблицу значений, сортируем ее, и уже после в другом цикле удаляем все строки, индексы которых мы «накопили».
Решая эту же задачу используя цикл «Для» можно изменить конструкцию, помня о возможности изменять текущее значение итератора и границы такого цикла. Так как удаление строк подразумевает смещение этих строк в таблице, то необходимо на каждой итерации проверять границу (максимальный индекс) таблицы, чтобы не попасть в ошибку «Индекс находится за границами массива». Напомним, индексы начинаются с нуля и заканчиваются «КоличествоЭлементов – 1»). Также в процессе реализации алгоритма был использован оператор «Продолжить», чтобы не проверять первую строку, уникальность которой проверять не требуется. Рассмотрим:
ДляИтератор= 0 ПоТабличнаяЧасть.Количество()- 1 Цикл
ЕслиИтератор= 0 Тогда
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
ЕслиТабличнаяЧасть[Итератор].Номенклатура=ПроверяемоеЗначениеТогда
ТабличнаяЧасть.Удалить(Итератор);
Итератор=Итератор- 1;
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
Удаляя в цикле элемент, мы возвращаем итератор в предыдущее состояние, чтобы, преодолев «КонецЦикла», итератор вернулся к значению для проверки следующей строки, но которая была смещена на одну позицию. Таким образом пропущенных строк теперь не будет.
Теперь решим задачу используя цикл «Пока». В данном случае будет несколько изменений. Во-первых, если мы используем итератор, то для условия требуется объявить переменную итератора еще до начала цикла. Во-вторых, необходимо каждый раз получать количество элементов, так как цикл позволяет при обходе коллекции сразу и удалять эти элементы коллекции. Таким образом мы, объявив итератор перед циклом, будем каждый раз его сравнивать с количеством элементов. Если итератор станет равен или больше количества (мы помним, что максимальный индекс — это всегда «КоличествоЭлементов – 1»), значит мы вышли «за границы массива», т.е. обошли всю коллекцию. В-третьих, чтобы не войти в рекурсию необходимо искусственно «двигать» итератор. А значит мы будем его двигать не каждый шаг, а только когда значение не равно проверяемому и не является первым, т.е. мы нашли следующее уникальное значение.
ПокаИтератор<ТабличнаяЧасть.Количество()Цикл
ЕслиИтератор= 0 Тогда
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
Итератор=Итератор+ 1;
ЕслиТабличнаяЧасть[Итератор].Номенклатура=ПроверяемоеЗначениеТогда
ТабличнаяЧасть.Удалить(Итератор);
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
Итератор=Итератор+ 1;
Таким образом, в цикле «Для» мы «двигали» итератор назад только тогда, когда находили неуникальный элемент, который требовалось удалить. В Цикле «Пока» мы делаем наоборот: «двигаем» итератор каждый раз кроме ситуации, когда будет найден неуникальный элемент.
В примерах реализации задачи были рассмотрены все три цикла и их возможности.
Но в начале статьи было сказано о Запросах в цикле, и этот момент также будет кратко рассмотрен на примере других вариантов использования цикла «Пока».
Так как результат Запроса является также таблицей, то и обход этой коллекции выполняется с учетом этого. Помним, что выборка детальных записей выполняется «порционно», а значит условием будет «Выборка.Следующий()», что говорит об обходе до тех пор, пока коллекция, состоящая из строк, не закончится:
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
Поговорим о понятии «Запрос в цикле». Чаще имеется в виду не реализацию Запроса, который находится внутри какого-либо цикла, а нахождение операторов или обращений к базе данных в виде «Неуправляемого Запроса»: обращение к данным с помощью Объектной модели через точку. Таким образом можно однозначно разделить Запрос на «Управляемый» (реализуемый конструкцией «Запрос = Новый Запрос(<ТекстЗапроса>») и на «Неуправляемый» (реализуемый Объектной моделью, например, «Объект.Контрагент.ДоговорКонтрагента»). Не рекомендуется делать «неуправляемые» Запросы как самостоятельно, так и в циклах (особенно в циклах).
Рассмотрим два варианта использования цикла «Пока» для Запросов. В первом варианте рассмотрим «управляемые» Запросы в цикле и двойном цикле (используется при описании алгоритма списания себестоимости). Во втором же варианте рассмотрим «неуправляемые» Запросы в цикле и как их можно избежать.
В данном варианте можно использовать цикл «Пока» для обхода коллекции Результата Запроса. При этом никакие ограничители указывать не требуется. Метод «Выборка.Следующий()» не позволит выйти за границы, а значит и проблемы рекурсии не будет:
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
Второй способ реализации будет - Запрос в двойном цикле, что опять же не приводит к проблемам рекурсии и «Запроса в цикле»:
Результат=Запрос.Выполнить();
ВыборкаНоменклатура=Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
ПокаВыборкаНоменклатура.Следующий()Цикл
ЕслиВыборкаНоменклатура.Количество>ВыборкаНоменклатура.КоличествоРегистра
Отказ=Истина;
ЕслиНеОтказТогда
ОсталосьСписать=ВыборкаНоменклатура.Количество;
ВыборкаДетальныеЗаписи=ВыборкаНоменклатура.Выбрать();
ПокаОсталосьСписать> 0 ИВыборкаДетальныеЗаписи.Следующий()Цикл
ПеременнаяКоллекцияДвижений=Мин(ОсталосьСписать,
ВыборкаДетальныеЗаписи.КоличествоРегистра);
ОсталосьСписать=ОсталосьСписать-ПеременнаяКоллекцияДвижений;
Рассмотрим саму проблему «Запроса в цикле», а именно, что таковым является чаще всего:
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
НоваяСтрока=ТабличнаяЧасть.Добавить();
НоваяСтрока.Номенклатура=ВыборкаДетальныеЗаписи.Номенклатура;
НоваяСтрока.НоменклатураНаименование=ВыборкаДетальныеЗаписи.Номенклатура.Представление;
В данном примере, «ВыборкаДетальныеЗаписи.Номенклатура.Представление» — это и есть «неуправляемый» Запрос, тот самый «Запрос в цикле». Смысл заключается в следующем: при выполнении цикла требуется, чтобы данные были однозначные, по возможности без каких-то дополнительных обращений через точку (без использования Объектной модели внутри Табличной модели).
Именно это считается грубым и непрофессиональным использованием цикла, так как через запрос можно получить все необходимые данные для использования цикла. Например, просто добавив в текст запроса получение «Представления» и работа с ним внутри уже выборки:
Запрос=НовыйЗапрос("ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Представление КАК Представление
| Справочник.Номенклатура КАК Номенклатура");
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
НоваяСтрока=ТабличнаяЧасть.Добавить();
НоваяСтрока.Номенклатура=ВыборкаДетальныеЗаписи.Ссылка;
НоваяСтрока.НоменклатураНаименование=ВыборкаДетальныеЗаписи.Представление;
Более подробно о Запросах можно прочитать в других статьях, где и раскрываются особенности «управляемых» Запросов и «неуправляемых» Запросов.
>Запрос в цикле. Когда он выгоднее, чем запрос вне цикла?
Правильный ответ - никогда.
Если факт противоречит этому, это говорит о том, что неверно организован запрос вне цикла.
(1) неправильный ответ. в моем случае выгоднее именно в цикле. ибо сумма квадратов всегда меньше квадрата суммы
(1) >> Правильный ответ - никогда.
А обоснования данного тезиса у Вас есть?
(0) Например, запрос вызывается с такими разными наборами параметров, для которых неэффективно получать всё в одном запросе.
(3) Не понимаю причём тут это.
(4) Конечно. Как минимум не тратится время на иннициацию запроса, передачу параметров и т.п.
Подчеркну, что сам вопрос подразумевает получение одних и тех же данных единым запросом, или несколькими однотипными запросами к одному источник в цикле. Речь не идёт о каком-то прикладном цикле, когда сам цикл скорее является инструментом для перебора разных источников.
Т.е. если я устрою цикл из 4-х элементов (УУ, БУ, НУ, МУ) и далее отдельно формирую запросы к разным данным, я не считаю это "запросом в цикле".
(0) Запрос в цикле практически всегда лучше, чем решение той же задачи через запрос вне цикла.
1. Лучше использовать явный цикл, чем неявный. Это понятнее и нагляднее.
2. Оптимизировать надо там, где есть потребность в оптимизации.
3. Незачем потакать лени разработчиков платформы. В чем проблема выносить запросы из циклов на уровне платформы? Разве не этим она должна заниматься?
(0) Когда есть составное поле и ты не знаешь все его типы до выполнения запроса.
(8) Хорошо скажем так, я под "запросом в цикле" подразумеваю один и тот же запрос (или с очень минимальными правками) в который передаются разные параметры, перебираемые в цикле. Т.е. цель цикла - именно в переборе параметров.
В остальных случаях, когда цикл нужен просто для более удобной и универсальной компоновки текста запроса, перебора источников и т.п. я не считаю это "запросом в цикле", это просто метод программирования. И тут ответ на вопрос в (0) зависит от очень многих факторов, что на него просто невозможно ответить "вообще и в целом".
Но однозначно сказать что запрос в цикле это плохо - нельзя!
(0) Он выгоднее тогда, когда надо решить задачу, но ты не знаешь как сделать без него.
(10) Надо строить запрос кодом.
(9) Я надеюсь это ты тралишь, а не выражаешь свое профессиональное мнение.
Однозначно, выборка данных единым запросом лучше, чем дергание СУБД постоянными запросами поменьше, ибо возрастает вероятность блокировок.
Выбрать Продажи.Регистратор.Номер как НомерДокумента
и постобработкой получить номера, с числом запросов= количеству различных типов регистратора.
Циклы в 1С и в любом другом языке программирования используются для повторения заданной последовательности действий. Цикл может выполняться фиксированное количество раз, пока выполняется условие или при обходе элементов коллекции.
Циклы с фиксированным количеством проходов.
Циклы с фиксированным, заранее известным количеством проходов (итераций) имеют следующий синтаксис:
Цикл выполнится столько раз сколько указано в переменной НужноеКоличествоИтераций которая должна быть числовой, значение сч будет увеличиваться на каждой итерации на 1, можно самому в теле цикла указывать содержимое переменной сч управляя количеством проходом, также нужно иметь ввиду, что на каждом витке цикла содержимое этой переменной будет увеличиваться на 1 в любом случае вне зависимости от ваших действий. Можно просто указать:
если нужно что бы цикл просто сделал жестко указанное количество итераций.
Циклы по условию.
Циклы по условию имеют следующий ситаксис:
Здесь цикл будет выполняться до тех пор, пока будет выполняться условие. Очень просто организовать бесконечный цикл не допуская увеличения переменной А больше 99. Такой цикл "повешает" 1С и его разумеется имеет смысл избегать, если вы конечно не используете оператор Прервать .
Циклы для обхода коллекций.
Любую коллекцию можно обойти в цикле следующим образом:
Коллекциями могут быть: строки и колонки табличных частей документов\справочников и таблиц значений, массивы и другие типы данных.
Операторы управляющие ходом цикла.
Продолжить
Оператор Продолжить делает следующий виток цикла передавая управление на начало цикла, таким образом пропуская команды которые идут ниже. Например в следующем случае:
значение переменной а будет увеличиваться только в нечетных проходах цикла, но цикл сделает 100 проходов без исключений.
Прервать
Оператор Прервать прерывает выполнение цикла, передавая выполнение на команды которые идут после цикла не рекомендуется его использовать без необходимости, так как он затрудняет анализ кода расположенного в цикле.
Использование функций в теле цикла.
Использование функций в тебе цикла считается дурным тоном если эта функция будет каждый раз возвращать одно и то же значение. Не имеет смысла каждый раз выполнять один и тот же код если результат его выполнения не изменяется, в таких случаях функцию необходимо выполнить до цикла поместив результат в переменную и далее в цикле использовать эту переменную.
Использование запросов в теле цикла.
Использование запросов в теле цикла также считается недопустимым. Так как при каждом витке цикла делается запросе к базе данных, а использование запросов подразумевает под собой идеологию "Все нужные данные должны быть получены одним запросом". Если есть возможность, следует проанализировать какие данные будут нужны в цикле, сделать запрос до цикла и в цикле пользоваться уже результатами ранее созданного запроса. Здесь следует добавить пример.
Правильное использование Циклов в языке 1С может помочь решить поставленные задачи и упростить алгоритмы. Неправильный подход к циклам усложняет программный код и алгоритм, делает его менее «прозрачным» и ясным для других специалистов, уменьшает скорость работы программного кода. Иногда некорректное использование в шутку называют «противозаконным», хотя это и не смешно (особенно это касается всем известных Запросов в циклах). Хоть и считается что Запрос в цикле — это грубейшая ошибка, но бывают моменты, когда иначе просто не сделать.
В данной статье не будет примера, так как подобные ситуации встречаются достаточно редко и зависят от контекста использования, скорости работы алгоритма, оптимальности алгоритма, других возможных факторов.
1. 3 вида циклов
В данной статье будет рассмотрен не столько синтаксис, сколько разумное использование циклов и применение для упрощения кода и его визуальной оптимизации.
Всего существует 3 вида циклов: «Для», «Для каждого», «Пока». О каждом из них можно прочитать в Синтакс-помощнике.
«Для» - циклическое повторение операторов внутри «Цикл -КонецЦикла», где можно использовать итератор с указываемыми границами (шаг всегда равен единице). При этом границы можно регулировать внутри самого цикла. Главное не забывать, что границы должны быть типа Число.
«Для каждого» - обход коллекции значений. Каждая итерация – последующий элемент коллекции, обход будет до тех пор, пока элементы в коллекции не закончатся. Каждый элемент коллекции — это либо элемент, либо строка с элементами. Например, для Массива – это Элемент, для Таблицы значений – это Строка (обращение к элементу в строке будет через точку).
«Пока» - циклическое повторение, пока выполняется условие в выражении. Например, «Пока Итератор > 0 Цикл». Для исключения рекурсии рекомендуется после создания цикла сразу вписать изменение выражения, например, «Итератор = Итератор – 1;».
Также для работы циклов существуют два оператора: «Прервать» и «Продолжить»:
1. «Прервать» - используется для выхода из цикла на любом моменте выполнения.
2. «Продолжить» - используется для перехода в начало цикла пропуская все, что находится после оператора «Продолжить».
2. Примеры использования циклов
Теперь можно рассмотреть несколько примеров с использованием разных циклов. Примеры только показывают, как можно использовать циклы, и не претендуют на единственно верный вариант решения.
Задача: необходимо перебрать табличную часть 1С и удалить дублирующие строки, оставив только первые встречаемые элементы справочника Номенклатура.
Данную задачу можно решить циклом «Для каждого». При использовании «Для каждого» обращение будет к элементам коллекции – Строкам.
Теперь определимся с методом решения. Следует помнить, что, удаляя строки из таблицы все последующие смещаются на количество удаленных строк. А это значит, что, используя «Для каждого», необходимо сохранить номер строки или индекса, чтобы после прохождения всей коллекции удалить необходимые строки в другом уже цикле. Первую строку мы просто сохраняем как проверяемое значение, так как первая строка всегда уникальна. Именно здесь можно использовать оператор «Продолжить», для пропуска первой строки на всякие проверки.
Так как нам необходимо удалять элементы с конца, то необходима сортировка по убыванию. А сортировка возможна в таблицах типа СписокЗначений, ТаблицаЗначений и прочие (помним о том, что Массив не сортируется). Значит для реализации задачи необходимо объявить переменную с одним из вышеуказанных типов. Рассмотрим:
УдаляемыеСтроки=НовыйТаблицаЗначений;
УдаляемыеСтроки.Колонки.Добавить("Индекс");
ДлякаждогоСтрокаИзТабличнаяЧастьЦикл
ЕслиСтрока.НомерСтроки= 1 Тогда
ПроверяемоеЗначение=Строка.Номенклатура;
ЕслиСтрока.Номенклатура=ПроверяемоеЗначениеТогда
ИндексСтроки=ТабличнаяЧасть.Индекс(Строка);
УдаляемаяСтрока=УдаляемыеСтроки.Добавить();
УдаляемаяСтрока.Индекс=ИндексСтроки;
ПроверяемоеЗначение=Строка.Номенклатура;
УдаляемыеСтроки.Сортировать("Индекс Убыв");
ДлякаждогоСтрокаИзУдаляемыеСтрокиЦикл
ТабличнаяЧасть.Удалить(Строка.Индекс);
Для удаления элементов с конца мы заполняем, как указано в примере Таблицу значений, сортируем ее, и уже после в другом цикле удаляем все строки, индексы которых мы «накопили».
Решая эту же задачу используя цикл «Для» можно изменить конструкцию, помня о возможности изменять текущее значение итератора и границы такого цикла. Так как удаление строк подразумевает смещение этих строк в таблице, то необходимо на каждой итерации проверять границу (максимальный индекс) таблицы, чтобы не попасть в ошибку «Индекс находится за границами массива». Напомним, индексы начинаются с нуля и заканчиваются «КоличествоЭлементов – 1»). Также в процессе реализации алгоритма был использован оператор «Продолжить», чтобы не проверять первую строку, уникальность которой проверять не требуется. Рассмотрим:
ДляИтератор= 0 ПоТабличнаяЧасть.Количество()- 1 Цикл
ЕслиИтератор= 0 Тогда
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
ЕслиТабличнаяЧасть[Итератор].Номенклатура=ПроверяемоеЗначениеТогда
ТабличнаяЧасть.Удалить(Итератор);
Итератор=Итератор- 1;
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
Удаляя в цикле элемент, мы возвращаем итератор в предыдущее состояние, чтобы, преодолев «КонецЦикла», итератор вернулся к значению для проверки следующей строки, но которая была смещена на одну позицию. Таким образом пропущенных строк теперь не будет.
Теперь решим задачу используя цикл «Пока». В данном случае будет несколько изменений. Во-первых, если мы используем итератор, то для условия требуется объявить переменную итератора еще до начала цикла. Во-вторых, необходимо каждый раз получать количество элементов, так как цикл позволяет при обходе коллекции сразу и удалять эти элементы коллекции. Таким образом мы, объявив итератор перед циклом, будем каждый раз его сравнивать с количеством элементов. Если итератор станет равен или больше количества (мы помним, что максимальный индекс — это всегда «КоличествоЭлементов – 1»), значит мы вышли «за границы массива», т.е. обошли всю коллекцию. В-третьих, чтобы не войти в рекурсию необходимо искусственно «двигать» итератор. А значит мы будем его двигать не каждый шаг, а только когда значение не равно проверяемому и не является первым, т.е. мы нашли следующее уникальное значение.
ПокаИтератор<ТабличнаяЧасть.Количество()Цикл
ЕслиИтератор= 0 Тогда
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
Итератор=Итератор+ 1;
ЕслиТабличнаяЧасть[Итератор].Номенклатура=ПроверяемоеЗначениеТогда
ТабличнаяЧасть.Удалить(Итератор);
ПроверяемоеЗначение=ТабличнаяЧасть[Итератор].Номенклатура;
Итератор=Итератор+ 1;
Таким образом, в цикле «Для» мы «двигали» итератор назад только тогда, когда находили неуникальный элемент, который требовалось удалить. В Цикле «Пока» мы делаем наоборот: «двигаем» итератор каждый раз кроме ситуации, когда будет найден неуникальный элемент.
В примерах реализации задачи были рассмотрены все три цикла и их возможности.
Но в начале статьи было сказано о Запросах в цикле, и этот момент также будет кратко рассмотрен на примере других вариантов использования цикла «Пока».
Так как результат Запроса является также таблицей, то и обход этой коллекции выполняется с учетом этого. Помним, что выборка детальных записей выполняется «порционно», а значит условием будет «Выборка.Следующий()», что говорит об обходе до тех пор, пока коллекция, состоящая из строк, не закончится:
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
Поговорим о понятии «Запрос в цикле». Чаще имеется в виду не реализацию Запроса, который находится внутри какого-либо цикла, а нахождение операторов или обращений к базе данных в виде «Неуправляемого Запроса»: обращение к данным с помощью Объектной модели через точку. Таким образом можно однозначно разделить Запрос на «Управляемый» (реализуемый конструкцией «Запрос = Новый Запрос(<ТекстЗапроса>») и на «Неуправляемый» (реализуемый Объектной моделью, например, «Объект.Контрагент.ДоговорКонтрагента»). Не рекомендуется делать «неуправляемые» Запросы как самостоятельно, так и в циклах (особенно в циклах).
Рассмотрим два варианта использования цикла «Пока» для Запросов. В первом варианте рассмотрим «управляемые» Запросы в цикле и двойном цикле (используется при описании алгоритма списания себестоимости). Во втором же варианте рассмотрим «неуправляемые» Запросы в цикле и как их можно избежать.
В данном варианте можно использовать цикл «Пока» для обхода коллекции Результата Запроса. При этом никакие ограничители указывать не требуется. Метод «Выборка.Следующий()» не позволит выйти за границы, а значит и проблемы рекурсии не будет:
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
Второй способ реализации будет - Запрос в двойном цикле, что опять же не приводит к проблемам рекурсии и «Запроса в цикле»:
Результат=Запрос.Выполнить();
ВыборкаНоменклатура=Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
ПокаВыборкаНоменклатура.Следующий()Цикл
ЕслиВыборкаНоменклатура.Количество>ВыборкаНоменклатура.КоличествоРегистра
Отказ=Истина;
ЕслиНеОтказТогда
ОсталосьСписать=ВыборкаНоменклатура.Количество;
ВыборкаДетальныеЗаписи=ВыборкаНоменклатура.Выбрать();
ПокаОсталосьСписать> 0 ИВыборкаДетальныеЗаписи.Следующий()Цикл
ПеременнаяКоллекцияДвижений=Мин(ОсталосьСписать,
ВыборкаДетальныеЗаписи.КоличествоРегистра);
ОсталосьСписать=ОсталосьСписать-ПеременнаяКоллекцияДвижений;
Рассмотрим саму проблему «Запроса в цикле», а именно, что таковым является чаще всего:
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
НоваяСтрока=ТабличнаяЧасть.Добавить();
НоваяСтрока.Номенклатура=ВыборкаДетальныеЗаписи.Номенклатура;
НоваяСтрока.НоменклатураНаименование=ВыборкаДетальныеЗаписи.Номенклатура.Представление;
В данном примере, «ВыборкаДетальныеЗаписи.Номенклатура.Представление» — это и есть «неуправляемый» Запрос, тот самый «Запрос в цикле». Смысл заключается в следующем: при выполнении цикла требуется, чтобы данные были однозначные, по возможности без каких-то дополнительных обращений через точку (без использования Объектной модели внутри Табличной модели).
Именно это считается грубым и непрофессиональным использованием цикла, так как через запрос можно получить все необходимые данные для использования цикла. Например, просто добавив в текст запроса получение «Представления» и работа с ним внутри уже выборки:
Запрос=НовыйЗапрос("ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Представление КАК Представление
| Справочник.Номенклатура КАК Номенклатура");
РезультатЗапроса=Запрос.Выполнить();
ВыборкаДетальныеЗаписи=РезультатЗапроса.Выбрать();
ПокаВыборкаДетальныеЗаписи.Следующий()Цикл
НоваяСтрока=ТабличнаяЧасть.Добавить();
НоваяСтрока.Номенклатура=ВыборкаДетальныеЗаписи.Ссылка;
НоваяСтрока.НоменклатураНаименование=ВыборкаДетальныеЗаписи.Представление;
Более подробно о Запросах можно прочитать в других статьях, где и раскрываются особенности «управляемых» Запросов и «неуправляемых» Запросов.
&НаСервере
Процедура ОтборЗаписейВЗапросеПоОпределенномуЗначениюРеквизиту ()
// Выборка котрагентов, у которых значение реквизита Резидентство = НеРезидент
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| СтранаРегистрации,
| ЮрАдрес,
| Резидентство
|ИЗ
| Справочник.Контрагенты
|ГДЕ
| Резидентство = ЗНАЧЕНИЕ(Перечисление.Резидентство.НеРезидент)
|" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ОтборЗаписейВЗапросеПоОпределенномуЗначениюБухСчету ()
// Выборка проводок с суммами и количеством по дебету бухгалтерского счёта 01.01,
// через обращение по предопределенному имени "ОсновныеСредстваВОрганизации"
Запрос = Новый Запрос ( "ВЫБРАТЬ
| СчетДт,
| СчетКт,
| Количество,
| Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный
|ГДЕ
| СчетДт = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОсновныеСредстваВОрганизации)" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ОпределениеПеременойТипаДатаВЗапросе ()
// Указывать дату можно прямо в запросе или передавать через параметр
Запрос = Новый Запрос ( "ВЫБРАТЬ
| ДАТАВРЕМЯ(2020, 05, 30, 23, 00, 00) КАК ДоНачалаЛетаОсталсяОдинЧас" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ФункцииДляРаботыСДатамиВЗапросе ()
Запрос . УстановитьПараметр ( "ВыбраннаяДата" , '20200607' );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ИспользованиеОператораВЫБОРвЗапросе ()
// Выборка материалов с добавлением поля "ОписаниеПлотности" для новой градации
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| Плотность,
| ВЫБОР
| КОГДА Плотность 2 ТОГДА
| ""Средняя плотность""
| ИНАЧЕ
| ""Очень плотный материал""
|
| КОНЕЦ КАК ОписаниеПлотности
| ИЗ
| Справочник.Материалы" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ИспользованиеЗначенияНеопределеноВЗапросе ()
// Выборка контрагентов у которых есть реквизит составного типа "ДокументыДвижения" и он не заполнен
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| ДокументыДвижения
|ИЗ
| Справочник.Контрагенты
|ГДЕ
| ДокументыДвижения = Неопределено" );
// Неопределено - применяется когда нужно использовать пустое значение
// не принадлежащее ни к одному другому типу
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ПроверкаЗначенияНаСоответствиеСсылочномуТипуВЗапросе ()
// Выборка бухг.проводок, у которых регистратором является документ типа "ПоступлениеМатериалов"
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Регистратор,
| СчетДт,
| СчетКт,
| Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный
|ГДЕ
| Регистратор ССЫЛКА Документ.ПоступлениеМатериалов" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ПроверкаЗначенияНаВхождениеВДиапазон ()
// Выборка материалов с весом от 3500 до 7500
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| Вес
|ИЗ
| Справочник.Материалы
|ГДЕ
| Вес МЕЖДУ 3500 И 7500" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ПроверкаЗначенияНаВхождениеВСписок ()
// Выборка материалов произведенных на Гомелькабель и Гомельстекло
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| Производитель
|ИЗ
| Справочник.Материалы
|ГДЕ
| Производитель В (&Гомелькабель, &Гомельстекло)" ); // у оператора "В" есть вариант "В ИЕРАРХИИ" (в списке групп)
Запрос . УстановитьПараметр ( "Гомелькабель" , Справочники . Контрагенты . НайтиПоНаименованию ( "Гомелькабель" ));
Запрос . УстановитьПараметр ( "Гомельстекло" , Справочники . Контрагенты . НайтиПоНаименованию ( "Гомельстекло" ));
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ПроверкаВЗапросеНаОтсутствиеРеквизита ()
// Выборка контрагентов у которых нет реквизита "Резидентство"
// (понятно, что этими элементами будут только группы)
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| Резидентство
|ИЗ
| Справочник.Контрагенты
|ГДЕ
| Резидентство ЕСТЬ NULL" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура ПроверкаВЗапросеИЗамена NULL НаДрЗначение ()
// Выборка контрагентов у которых нет реквизита "Резидентство"
// и вывод фразы "NULL", если "Резидентство" ЕСТЬ NULL
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| ЕСТЬNULL(Резидентство, ""NULL"")
|ИЗ
| Справочник.Контрагенты" );
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
// Функция запроса ЕстьNull обычно используется для избавления от значений типа Null
// для числовых полей запроса. В ряде случаев, например полного соединения
// двух таблиц функция ЕстьNull (ПараметрN1,ПараметрN2) может с успехом заменить
// конструкцию ВЫБОР КОГДА . ТОГДА ..ИНАЧЕ ….КОНЕЦ, когда для какого-либо поля
// значения NULL могут быть как в первой таблице, так и во второй
// такая конструкция позволяет получать не Null значение для поля.
//
// Но надо помнить, что в отличие от условного оператора ВЫБОР функция ЕстьNull
// приводит тип второго аргумента к типу первого аргумента, что нужно учитывать,
// если типы аргументов отличаются!
&НаСервере
Процедура ПолучениеПустойСсылкиВЗапросе ()
// Выборка контрагентов у которых есть реквизит "Резидентство" и он не заполнен
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование,
| Резидентство
|ИЗ
| Справочник.Контрагенты
|ГДЕ
| Резидентство = ЗНАЧЕНИЕ(Перечисление.Резидентство.ПустаяСсылка)" );
// Так же пишутся:
// ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
// ЗНАЧЕНИЕ(Документ.ПоступлениеМатериалов.ПустаяСсылка).
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
&НаСервере
Процедура СравнениеВЗапросеСтрокиСШаблоном ()
// Выборка контрагентов которые содержат слова, начинающиеся на " Гомель"
Запрос = Новый Запрос ( "ВЫБРАТЬ
| Наименование
|ИЗ
| Справочник.Контрагенты
|ГДЕ
| Наименование ПОДОБНО ""%[ ][Г][о][м][е][л][ь]_%""" );
// Параметры строки шаблона:
// % - любое количество произвольных символов
// _ - один произвольный символ
// [] - любой одиночный символ, перечисленный внутри скобок
// [^] - любой одиночный символ, кроме тех, что внутри скобок после ^
РезультатЗапроса = Запрос . Выполнить (); Записи = РезультатЗапроса . Выбрать ();
Пока Записи . Следующий () Цикл
// Обход результата запроса по каждой записи в полученной выборке
КонецЦикла;
Читайте также: