При написании встроенных («чёрных») запросов в платформе 1С:Предприятие 7.7 разработчики нередко сталкиваются с ситуацией, когда условия фильтрации данных в секции Условие(...) отрабатывают некорректно: строки, которые должны быть исключены, всё равно попадают в результат выборки. Рассмотрим типичную ситуацию на примере регистра «Прибыль» и разберем, почему условия сравнения с перечислениями могут игнорироваться, а также как правильно выстраивать логику отбора.
Проанализируем пример запроса, в котором возникла ошибка:
Период с НачДата по ДатаЗП;
Пол = Регистр.Прибыль.Товар.ПолТовара;
Тов = Регистр.Прибыль.Товар;
Скл = Регистр.Прибыль.Склад;
Гор = Регистр.Прибыль.Склад.Город;
Поставщик = Регистр.Прибыль.Поставщик;
Фирма = Регистр.Прибыль.Фирма;
Клиент = Регистр.Прибыль.Клиент;
Себест = Регистр.Прибыль.Стоимость;
Выруч = Регистр.Прибыль.Выручка;
КолПрод = Регистр.Прибыль.Количество;
Группировка Пол;
Группировка Тов Без Групп;
Группировка Поставщик Без Групп;
Функция Кол = Сумма(КолПрод);
Функция Себ = Сумма(Себест);
Функция Выр = Сумма(Выруч);
Условие (Тов в СписокТоваров);
Условие (Пол <> Перечисление.Пол.Мужской);
Условие (Пол <> Перечисление.Пол.Женский);
Ожидаемое поведение: исключить из выборки товары с признаками мужского и женского пола. Фактический результат: в отчет продолжают выводиться как мужские, так и женские товары. Выясним основные причины такого поведения встроенного механизма запросов платформы 1С 7.7.
Самая распространенная причина ошибки — использование в условии значения из другого перечисления. В конфигурациях 1С 7.7 могут сосуществовать похожие объекты метаданных, например, Перечисление.Пол (применяется для физических лиц или сотрудников) и Перечисление.ПолТовара (реквизит справочника номенклатуры).
Платформа 1С 7.7 строго проверяет типы объектов. Сравнение значения типа Перечисление.ПолТовара.Мужской со значением Перечисление.Пол.Мужской всегда возвращает результат «не равно» (Ложь при сравнении на равенство и Истина при сравнении через <>). Вследствие этого условие Пол <> Перечисление.Пол.Мужской выполняется абсолютно для всех строк регистра, и фильтрация не срабатывает.
Для исправления достаточно указать правильный идентификатор перечисления, соответствующий типу реквизита номенклатуры:
Условие (Пол <> Перечисление.ПолТовара.Мужской);
Условие (Пол <> Перечисление.ПолТовара.Женский);
Когда требуется исключить сразу несколько значений, перечисление условий через оператор <> может усложнять чтение кода. В 1С 7.7 несколько строк Условие(...) автоматически связываются логическим оператором И. Если логика усложняется, надежнее использовать передачу списка исключений через объект СписокЗначений с проверкой через отрицание.
Разберем по шагам, как реализовать этот подход:
НЕ (... В ...).Посмотрим на пример программного кода:
СписокИсключений = СоздатьОбъект("СписокЗначений");
СписокИсключений.Добавить(Перечисление.ПолТовара.Мужской);
СписокИсключений.Добавить(Перечисление.ПолТовара.Женский);
ТекстЗапроса = "
|Период с НачДата по ДатаЗП;
|Тов = Регистр.Прибыль.Товар;
|Пол = Регистр.Прибыль.Товар.ПолТовара;
|Поставщик = Регистр.Прибыль.Поставщик;
|КолПрод = Регистр.Прибыль.Количество;
|Группировка Пол;
|Группировка Тов Без Групп;
|Функция Кол = Сумма(КолПрод);
|Условие (Тов В СписокТоваров);
|Условие (НЕ (Пол В СписокИсключений));
|";
Если реквизит перечисления у товара не заполнен, попытка сравнить его со пустой строкой "" приведет к ошибке типизации. Не заполненный реквизит типа перечисление содержит пустое значение своего типа, а не пустую строку.
Для отбора строк с незаполненным или, наоборот, заполненным реквизитом во встроенном языке запросов 1С 7.7 применяется системная функция ПустоеЗначение():
// Отбор только тех товаров, у которых пол не указан:
Условие (ПустоеЗначение(Пол) = 1);
// Отбор только тех товаров, у которых пол заполнен:
Условие (ПустоеЗначение(Пол) = 0);
Обратим внимание на обращение к реквизитам через точку в строке Пол = Регистр.Прибыль.Товар.ПолТовара;. В платформе 1С 7.7 разыменование через несколько точек не индексируется и приводит к тому, что при обработке каждой строки движений регистра платформа выполняет отдельное обращение к таблице справочника товаров.
Если таблица движений регистра содержит большой объем записей, запрос будет выполняться медленно. Чтобы ускорить обработку данных, рекомендуется выполнить предварительную выборку нужных позиций из справочника товаров в СписокЗначений и оставить в запросе только фильтр по самому товару:
СписокНужныхТоваров = СоздатьОбъект("СписокЗначений");
СпрТовары = СоздатьОбъект("Справочник.Номенклатура");
СпрТовары.ВыбратьЭлементы();
Пока СпрТовары.ПолучитьЭлемент() = 1 Цикл
Если СпрТовары.ЭтоГруппа() = 1 Тогда
Продолжить;
КонецЕсли;
// Проверяем условия по реквизитам заранее
Если (СпрТовары.ПолТовара <> Перечисление.ПолТовара.Мужской)
И (СпрТовары.ПолТовара <> Перечисление.ПолТовара.Женский) Тогда
СписокНужныхТоваров.Добавить(СпрТовары.ТекущийЭлемент());
КонецЕсли;
КонецЦикла;
// В самом запросе фильтруем только по отобранному списку:
// Условие (Тов В СписокНужныхТоваров);
Такой подход исключает множественные обращения к реквизитам справочника в цикле обработки регистра и гарантирует корректную отработку условий без риска несовпадения типов данных.