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