Неравенство в 1С: знак <>, оператор НЕ и отбор «Не равно»

· 9 мин

Содержание

«Не равно» в 1С записывается знаком <> — и в языке запросов, и во встроенном языке. Логическое отрицание целого условия пишут оператором НЕ. Отдельного символа != в 1С нет, двойного == тоже: равенство — это одиночный знак =.

Казалось бы, всё просто. Но ровно здесь спотыкаются и начинающие, и опытные: условие Поле <> Значение молча выбрасывает строки, где в поле лежит NULL, а отбор «Не равно» в динамическом списке ведёт себя не так, как ждёшь. Ниже — где какой оператор ставить, как не потерять пустые значения и как задать «не равно» программно.

Чем «не равно» записывают в запросе

В языке запросов 1С неравенство — это знак <>. Он сравнивает два значения и возвращает истину, когда они различаются.

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.ВидНоменклатуры <> &ВидУслуга

Запрос вернёт всю номенклатуру, кроме услуг. Параметр &ВидУслуга передаётся заранее через Запрос.УстановитьПараметр.

Рядом с <> в запросе работает логический оператор НЕ. Он отрицает не значение, а целое условие:

ГДЕ
    НЕ Номенклатура.ПометкаУдаления
    И НЕ (Номенклатура.Родитель = &Группа)

Разница практическая: <> ставят между двумя значениями, НЕ — перед булевым выражением. Запись НЕ (Поле = Значение) и Поле <> Значение дают один результат на непустых данных, но ведут себя по-разному, когда в поле попадает NULL (об этом ниже).

Ключевые слова запроса можно писать и по-английски: <> остаётся тем же, а НЕ превращается в NOT. Смешивать русский и английский синтаксис в одном запросе нельзя.

Исключить сразу несколько значений: НЕ В

Когда нужно отсеять не одно значение, а список, вместо цепочки <> ... И <> ... используют оператор НЕ В.

ГДЕ
    Номенклатура.ВидНоменклатуры НЕ В (&ВидУслуга, &ВидРабота)

В скобках может стоять не только перечень параметров, но и вложенный запрос:

ГДЕ
    Товары.Номенклатура НЕ В (
        ВЫБРАТЬ Запрет.Номенклатура ИЗ РегистрСведений.ЗапрещённаяКНоменклатура КАК Запрет
    )

Здесь прячется классическая ловушка. Если вложенный запрос в НЕ В вернёт хотя бы одну строку со значением NULL, внешний запрос отдаст пустой результат. Так работает трёхзначная логика: сравнение с NULL даёт не «ложь», а «неизвестно», и условие НЕ В перестаёт выполняться для всех строк.

Лечится это отсечением NULL в подзапросе:

Товары.Номенклатура НЕ В (
    ВЫБРАТЬ Запрет.Номенклатура
    ИЗ РегистрСведений.ЗапрещённаяКНоменклатура КАК Запрет
    ГДЕ Запрет.Номенклатура ЕСТЬ НЕ NULL
)

Оператор «не равно» во встроенном языке

Во встроенном языке 1С (в модулях, обработках, формах) неравенство — тот же знак <>:

Если Статус <> "Проведён" Тогда
    Сообщить("Документ ещё не проведён");
КонецЕсли;

Равенство здесь — одиночный =, а не ==. Это частая ошибка тех, кто пришёл из C-подобных языков: == и != в 1С не существуют, компилятор на них ругается.

Логическое отрицание — оператор НЕ (его же пишут с маленькой буквы, Не). Он отрицает булево выражение:

Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
    ВызватьИсключение "Не заполнен контрагент";
КонецЕсли;

Строго говоря, Не — это оператор, а не функция, хотя скобки после него ставить можно и часто нужно для читаемости: НЕ (А = Б И В > 0). Скобки меняют группировку, поэтому на сложных условиях их лучше расставлять явно.

Отрицание знака и отрицание условия взаимозаменяемы на простых проверках:

ЗаписьСмысл
А <> Бзначения различаются
НЕ (А = Б)то же самое для непустых значений
НЕ Флагбулев флаг равен Ложь
Не ЗначениеЗаполнено(Х)значение не заполнено

Для булевых переменных пишите НЕ Флаг, а не Флаг <> Истина: короче и читается как фраза.

Как «не равно» ведёт себя с NULL

Это главный источник «молчаливых» ошибок. В запросе NULL — это не пустая ссылка и не ноль, а «значения нет». Любое сравнение с NULL, включая <>, возвращает не истину и не ложь, а неопределённость.

Практическое следствие: условие Поле <> &Значение отбросит строки, где в поле лежит NULL, хотя интуитивно кажется, что «нет значения» — это точно «не равно заданному».

Чаще всего NULL появляется при соединениях (ЛЕВОЕ СОЕДИНЕНИЕ), когда для строки нет пары в присоединяемой таблице. Остаток по складу, которого нет в регистре, придёт как NULL, и фильтр по нему тихо выкинет строку.

Проверять наличие значения нужно отдельными операторами:

  • Поле ЕСТЬ NULL — значение отсутствует;
  • Поле ЕСТЬ НЕ NULL — значение есть.

А чтобы NULL участвовал в сравнении «не равно» как обычное значение, его заранее подменяют функцией ЕСТЬNULL:

ГДЕ
    ЕСТЬNULL(Остатки.Склад, ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)) <> &Склад

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

Если подменять значение не хочется, то же самое делают явным «или»:

ГДЕ
    Остатки.Склад <> &Склад
    ИЛИ Остатки.Склад ЕСТЬ NULL

Так строки с NULL вернутся в выборку, а условие «не равно» останется читаемым. Какой из двух приёмов выбрать — вопрос вкуса и того, нужно ли потом это значение показывать пользователем.

Во встроенном языке NULL и Неопределено — разные значения. Из базы приходит NULL, а реквизит формы или не найденный элемент коллекции чаще дают Неопределено. Проверка Значение <> NULL в коде не поймает Неопределено, и наоборот. Сравнивайте с тем типом, который реально приходит.

Отбор «Не равно» в списках и формах

В пользовательском режиме «не равно» задают через отбор. Откройте список (например, список документов «Реализация»), нажмите кнопку настройки списка в командной панели и добавьте отбор по нужному полю. В колонке «Вид сравнения» выберите значение Не равно.

Такой отбор исключит из списка строки с заданным значением. Рядом в выпадающем списке видов сравнения есть и родственные варианты:

  • Не равно — исключить одно значение;
  • Не в списке — исключить перечень значений;
  • Не в группе из списка — исключить элементы указанных групп иерархического справочника.

Для динамического списка на управляемой форме отбор настраивается там же, через стандартную команду настройки списка. Настройка сохраняется между сеансами, если список это допускает.

У отбора «Не равно» в списке та же особенность с пустыми значениями, что и в запросе. Если поле у части строк не заполнено, они могут не попасть в отфильтрованный список — проверьте это на своих данных, прежде чем делать выводы по отчёту.

Задать «не равно» программно

Когда отбор нужно установить из кода — при открытии формы, в отчёте на СКД или в обработке — используют вид сравнения НеРавно.

Для отбора компоновки данных (динамический список, СКД):

Отбор = Список.КомпоновщикНастроек.Настройки.Отбор;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Организация");
ЭлементОтбора.ВидСравнения   = ВидСравненияКомпоновкиДанных.НеРавно;
ЭлементОтбора.ПравоеЗначение = ВыбраннаяОрганизация;
ЭлементОтбора.Использование  = Истина;

Перечисление ВидСравненияКомпоновкиДанных содержит и остальные «отрицательные» варианты — НеРавно, НеВСписке, НеСодержит, НеВГруппе. Выбирайте тот, что соответствует задаче.

Для простого отбора в менеджере объекта (например, при выборке элементов) структуру отбора так напрямую не задать: отрицание проще вынести в запрос со знаком <> и получить выборку оттуда. Там, где доступен только отбор по равенству, условие «не равно» реализуют через запрос или через фильтрацию уже полученной коллекции в цикле.

Проверь себя

Проверьте себя
Как в языке запросов 1С правильно отобрать строки, где поле НЕ пустое (не NULL)?
Какой оператор исключит сразу несколько значений одним условием?
Есть ли в встроенном языке 1С оператор `!=`?

Отрицание текста и вхождения

«Не равно» часто нужно не для ссылок и чисел, а для строк — когда ищут значения, которые не совпадают с образцом или не содержат подстроку.

Для точного несовпадения строки работает обычный <>. Но если нужно отобрать то, что не похоже на шаблон, в запросе применяют отрицание оператора ПОДОБНО:

ГДЕ
    НЕ Контрагенты.Наименование ПОДОБНО "%ООО%"

Такой фильтр оставит контрагентов, в чьём наименовании нет сочетания «ООО». Символ % заменяет любую последовательность знаков, _ — ровно один знак.

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

Отрицание поиска по подстроке почти всегда вынуждает базу просматривать всю таблицу: по шаблону с % в начале индекс не работает. На больших справочниках такой отбор сочетайте с позитивным условием, которое сначала сузит выборку.

Частые ошибки и оптимизация

Разберём то, что реально ломает выборки и замедляет запросы.

Строки с пустым значением «пропадают». Самая частая жалоба. Причина — сравнение <> с NULL даёт неопределённость. Решение — ЕСТЬNULL(Поле, Умолчание) <> &Значение или явная проверка ЕСТЬ НЕ NULL.

Пустой результат у НЕ В с подзапросом. Если подзапрос вернул NULL, всё условие схлопывается. Добавляйте в подзапрос ГДЕ ... ЕСТЬ НЕ NULL.

== и != из других языков. В 1С равенство — одиночный =, неравенство — <>. Компилятор не примет C-подобный синтаксис.

Отрицание ссылки на пустое значение. Условие Контрагент <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) отберёт только заполненные строки — но если поле может быть NULL, оберните его в ЕСТЬNULL.

Про скорость. Условия с неравенством плохо ложатся на индексы. База данных умеет быстро находить «равно такому-то значению», но «всё, кроме» вынуждает по сути перебрать таблицу. Отсюда правила:

  • не стройте весь отбор на одном <> по большой таблице — добавьте позитивное условие (по дате, по организации), которое сузит выборку по индексу;
  • НЕ (Поле В (...)) и Поле НЕ В (...) оптимизатор обрабатывает по-разному на разных СУБД — на объёмных данных сравните план и время;
  • длинную цепочку <> И <> И <> заменяйте одним НЕ В (...) — и читается лучше, и оптимизатору проще.

На практике это выглядит так: отбор «все документы, кроме проведённых» по регистру за год лучше писать с датой впереди условия неравенства.

ГДЕ
    Документы.Дата МЕЖДУ &Начало И &Конец
    И Документы.Проведён <> ИСТИНА

Сначала база по индексу даты берёт узкий срез за период, и только внутри него проверяет неравенство. Если же оставить одно <>, СУБД переберёт все документы за всю историю базы.

Если неравенство работает по полю регистра, проверьте, есть ли это поле в индексе (измерение, реквизит с индексированием). Фильтр по неиндексированному полю на миллионах записей — почти всегда медленно, независимо от того, = там или <>.

Частые вопросы

В чём разница между `НЕ` и `<>`?
<> сравнивает два значения и говорит, различаются ли они. НЕ отрицает целое булево условие. Поле <> 5 и НЕ (Поле = 5) на непустых данных совпадают, но с NULL ведут себя по-разному.
Почему строки с незаполненным полем не попадают в результат при `<>`?
Потому что в поле лежит NULL, а сравнение NULL <> Значение возвращает не истину, а неопределённость. Оберните поле в ЕСТЬNULL(Поле, Умолчание) или добавьте условие ИЛИ Поле ЕСТЬ NULL.
Есть ли в 1С оператор `!=`, как в других языках?
Нет. И в запросах, и во встроенном языке неравенство пишется только знаком <>. Равенство — одиночный =, двойного == тоже нет.
Можно ли записать «не равно» английскими ключевыми словами?
В запросе знак <> одинаков для обоих диалектов, а оператор НЕ по-английски пишется NOT. Русский и английский синтаксис в пределах одного запроса не смешивают.
Как исключить сразу несколько значений одним условием?
Оператором НЕ В: Поле НЕ В (&А, &Б, &В). Это короче и быстрее, чем цепочка из <>, соединённых через И.
Как задать отбор «Не равно» из кода?
Через ВидСравненияКомпоновкиДанных.НеРавно в элементе отбора компоновщика настроек динамического списка или отчёта на СКД.