Почему условие НЕ В() в запросе 1С не срабатывает и как правильно его настроить

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

Работа с запросами в 1С часто преподносит сюрпризы, особенно когда дело касается отрицаний и сложных типов данных. Ситуация, когда условие НЕ В (&Список) возвращает пустой результат или работает некорректно, является классической проблемой, с которой сталкиваются даже опытные разработчики. В рамках данной статьи мы подробно разберем, почему это происходит, как СУБД обрабатывает такие запросы и как правильно реализовать исключение данных из отчета.

Разбор ситуации: почему запрос возвращает пустоту

Рассмотрим стандартную задачу: нам необходимо получить обороты по продажам, исключив определенных контрагентов. Автор темы столкнулся с тем, что при использовании условия в параметрах виртуальной таблицы ДокументПродажи.Контрагент НЕ В (&СписокИсключения), запрос выдает пустой результат, хотя данные в базе точно есть. Проанализируем основные причины такого поведения.

1. Проблема трехзначной логики SQL и значений NULL

В языке SQL (на котором базируются запросы 1С) существует три варианта логического результата: ИСТИНА, ЛОЖЬ и UNKNOWN (Неизвестно). Значение NULL при любом сравнении дает UNKNOWN. Условие Поле НЕ В (Значение1, Значение2) технически преобразуется системой в следующую конструкцию:

Поле <> Значение1 И Поле <> Значение2 И Поле <> Значение3...

Если хотя бы в одной строке поле принимает значение NULL, или если в самом списке &СписокИсключения затесалось значение NULL, результат всего выражения для этой строки (или всего запроса) становится неопределенным, и строка отсекается. В контексте задачи автора, если у документа (например, «Отчет о розничных продажах») вообще нет реквизита Контрагент, обращение через точку ДокументПродажи.Контрагент возвращает именно NULL. В итоге условие НЕ В отбрасывает такие записи целиком.

2. Обращение через точку к составным типам

Реквизит ДокументПродажи в регистре накопления «Продажи» обычно имеет составной тип. Когда мы пишем ДокументПродажи.Контрагент в параметрах виртуальной таблицы, платформа 1С вынуждена выполнить неявное левое соединение (LEFT JOIN) со всеми таблицами документов, входящих в этот тип. Если их 20 штук, будет сделано 20 соединений. Если у какого-то типа документа нет реквизита Контрагент, результатом соединения в этом поле будет NULL, что возвращает нас к проблеме, описанной в первом пункте.

Вариант решения №1: Использование измерений регистра (Рекомендуемый)

Проанализируем структуру типового регистра «Продажи» в УТ 10.3 или аналогичных решениях. В этом регистре уже есть готовое измерение Контрагент. Это ключевой момент. Вместо того чтобы «пробиваться» к данным через регистратор или документ продажи, следует использовать само измерение регистра.

Рассмотрим правильный пример настройки параметров виртуальной таблицы:


ВЫБРАТЬ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.КоличествоОборот КАК КоличествоОборот
ИЗ
    РегистрНакопления.Продажи.Обороты(
        &ДатаНач,
        &ДатаКон,
        Регистратор,
        НЕ Контрагент В (&СписокИсключения)
    ) КАК ПродажиОбороты

Почему это работает лучше?

  1. Отсутствие NULL: В измерении Контрагент для розничных продаж обычно хранится пустая ссылка (Справочники.Контрагенты.ПустаяСсылка), а не NULL. Пустая ссылка — это конкретное значение, которое корректно сравнивается со списком.
  2. Производительность: Измерения регистра всегда индексированы. Поиск по индексу измерения Контрагент происходит в разы быстрее, чем выполнение десятков неявных соединений через точку.
  3. Использование итогов: Виртуальная таблица оборотов может использовать предварительно рассчитанные таблицы итогов только тогда, когда фильтры накладываются на измерения. Обращение через точку к реквизиту регистратора заставляет систему сканировать физическую таблицу движений, что значительно замедляет отчет.

Вариант решения №2: Обработка NULL через ВЫБОР или ЕСТЬNULL

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

Посмотрим на пример кода:


ГДЕ
    ВЫБОР
        КОГДА ПродажиОбороты.Регистратор.Контрагент ЕСТЬ NULL ТОГДА ИСТИНА
        ИНАЧЕ НЕ ПродажиОбороты.Регистратор.Контрагент В (&СписокИсключения)
    КОНЕЦ

В данном случае мы явно говорим системе: «Если контрагента найти не удалось (это розница или другой тип документа), то считаем, что условие фильтрации пройдено (ИСТИНА). В противном случае проверяем вхождение в список». Это позволяет избежать «проглатывания» результатов из-за трехзначной логики SQL.

Вариант решения №3: Использование «Анти-соединения» (Anti-Join)

Для максимально надежной работы на огромных объемах данных, когда условие НЕ В может работать неоптимально, профессиональные разработчики используют ЛЕВОЕ СОЕДИНЕНИЕ с последующей проверкой на NULL. Разберем этот алгоритм по шагам:

  1. Поместим список исключаемых контрагентов во временную таблицу (ВТ).
  2. Соединим основную таблицу оборотов с этой ВТ по полю Контрагент.
  3. В условии ГДЕ оставим только те строки, для которых в правой таблице (ВТ) не нашлось соответствия.

Пример реализации:


// Сначала подготавливаем ВТ_Исключения
// ... (код заполнения временной таблицы)

ВЫБРАТЬ
    Продажи.Номенклатура,
    Продажи.КоличествоОборот
ИЗ
    РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, , ) КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Исключения КАК Исключения
    ПО Продажи.Контрагент = Исключения.Контрагент
ГДЕ
    Исключения.Контрагент ЕСТЬ NULL

Этот метод считается наиболее «чистым» с точки зрения SQL-запросов, так как он полностью контролирует поведение пустых значений и зачастую работает быстрее при больших размерах списка исключений.

Подведем итоги

Проанализировав проблему, мы выяснили, что основной виновник неработающего условия НЕ В — это неочевидное поведение NULL при обращениях через точку к составным полям. Для корректной работы отчетов следуйте этим правилам:

Использование измерения Контрагент напрямую в параметрах виртуальной таблицы — это самый быстрый и правильный путь для типовых конфигураций вроде УТ 10.3. Это не только решит проблему с исчезающими данными, но и значительно ускорит формирование вашего отчета.

← На главную