Как правильно проверить список условий с помощью логических операторов при обходе выборки?

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

При разработке на платформе 1С:Предприятие 8 программисты часто сталкиваются с необходимостью фильтрации данных внутри цикла обхода выборки. Казалось бы, простая задача — добавить еще одно условие исключения — может превратиться в проблему, если не учитывать законы логики и особенности взаимодействия кода с базой данных. Разберем подробно, как правильно строить логические выражения, почему важно избегать поиска ссылок внутри цикла и как перенести проверку условий на сторону сервера базы данных.

Проблема 1: Логические ошибки при использовании отрицания

Рассмотрим ситуацию, когда нам нужно обработать номенклатуру, которая не входит в несколько определенных групп. В исходном сообщении была сделана попытка использовать оператор ИЛИ с отрицанием НЕ. Проанализируем, почему выражение НЕ ВГруппе1 ИЛИ НЕ ВГруппе2 работает некорректно.

Согласно законам формальной логики (законам де Моргана), если мы хотим исключить попадание элемента в обе группы сразу, нам нужно использовать одно из двух эквивалентных выражений:

  1. Использование оператора И для отрицаний: НЕ Условие1 И НЕ Условие2. Это означает, что товар одновременно не должен быть в первой группе и не должен быть во второй.
  2. Использование оператора ИЛИ внутри общего отрицания: НЕ (Условие1 ИЛИ Условие2). Мы сначала объединяем группы в одно множество, а затем проверяем, что элемент в него не входит.

Если же написать НЕ Условие1 ИЛИ НЕ Условие2, то условие будет истинным всегда, так как один и тот же элемент номенклатуры не может находиться в двух разных родительских группах одновременно (у него только один непосредственный родитель). Он всегда будет "не в Группе 1" или "не в Группе 2", и программа пропустит все элементы без исключения.

Посмотрим на правильный пример исправления кода в цикле:


// Неправильно:
Если Единица = "796" И (НЕ ВГруппе1 ИЛИ НЕ ВГруппе2) Тогда ...

// Правильно (вариант 1):
Если Единица = "796" И (НЕ ВГруппе1 И НЕ ВГруппе2) Тогда ...

// Правильно (вариант 2):
Если Единица = "796" И НЕ (ВГруппе1 ИЛИ ВГруппе2) Тогда ...

Проблема 2: Производительность и поиск в цикле (Антипаттерн)

Одной из самых грубых ошибок является использование методов поиска, таких как НайтиПоКоду() или НайтиПоНаименованию(), непосредственно внутри условия Если при обходе выборки. Разберем ситуацию: если в вашей выборке 10 000 строк, и вы дважды вызываете НайтиПоКоду() в каждой итерации, система выполнит 20 000 лишних запросов к базе данных.

Проанализируем правильный алгоритм подготовки данных:

  1. Выносим поиск за пределы цикла. Найдем нужные группы один раз и сохраним их в переменные.
  2. Используем ссылки. Внутри цикла сравниваем уже имеющиеся ссылки, что происходит мгновенно в оперативной памяти.

Пример оптимизированного кода:


// 1. Подготовка данных ПЕРЕД циклом
ГруппаИсключение1 = Справочники.Номенклатура.НайтиПоКоду("0245");
ГруппаИсключение2 = Справочники.Номенклатура.НайтиПоКоду("К0244");

Пока ВыборкаНоменклатура.Следующий() Цикл
    ТекущаяНоменклатура = ВыборкаНоменклатура.Номенклатура;
    
    // 2. Быстрая проверка ссылок в памяти
    Если ТекущаяНоменклатура.ЕдиницаДляОтчетов.Код = "196" 
        И НЕ ТекущаяНоменклатура.ПринадлежитЭлементу(ГруппаИсключение1)
        И НЕ ТекущаяНоменклатура.ПринадлежитЭлементу(ГруппаИсключение2) Тогда
        
        // Выполняем действия
    КонецЕсли;
КонецЦикла;

Проблема 3: Проверка иерархии для множества групп

Если групп-исключений становится много (три, пять или десять), код превращается в "простыню" из условий И НЕ.... В этом случае эффективно использовать Массив или СписокЗначений. Однако метод ПринадлежитЭлементу() работает только с одной конкретной ссылкой.

Рассмотрим, как можно изящно решить эту задачу через проверку всей иерархии. Выясним причину, по которой иногда лучше использовать Соответствие (Map) для кэширования результатов проверки. Если у вас много товаров из одних и тех же папок, можно один раз проверить папку и запомнить результат, чтобы не вызывать ПринадлежитЭлементу() для каждого товара.

Решение через запрос: "Золотой стандарт" 1С

Самый эффективный способ, который рекомендуют эксперты, — это перенос всей логики условий непосредственно в текст запроса. Это позволяет базе данных самой отфильтровать нужные строки или пометить их специальным флагом. Выясним, как это реализовать с помощью конструкции ВЫБОР КОГДА.

Разберем по шагам создание вычисляемого поля в запросе:

  1. Передаем массив исключаемых групп в параметры запроса.
  2. В тексте запроса используем оператор В ИЕРАРХИИ.
  3. Создаем поле-флаг ЭтоНужныйТовар.

Пример запроса:


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

Запрос.УстановитьПараметр("МассивГруппИсключений", СписокГрупп);

Теперь при обходе выборки вам не нужно писать сложные логические конструкции. Мы просто анализируем готовый результат:


Пока Выборка.Следующий() Цикл
    Если Выборка.ПодходитПодУсловие Тогда
        // Логика для группы 1
    Иначе
        // Логика для остальных (ИНАЧЕ)
    КонецЕсли;
КонецЦикла;

Резюме

Проанализировав ситуацию, мы видим, что решение проблемы кроется в комплексном подходе. Во-первых, всегда проверяйте скобки и логические операторы (И вместо ИЛИ при множественном отрицании). Во-вторых, никогда не делайте запросы к базе (поиск по коду) внутри цикла. В-третьих, стремитесь перекладывать логику фильтрации на сторону СУБД через запросы — это делает ваш код быстрее, чище и профессиональнее.

← На главную