Неравенство в 1С: оператор <>, отрицание НЕ и ловушки с NULL
Содержание
В 1С «не равно» пишется двумя символами <>: Если Сумма <> 0 Тогда. Тот же оператор работает в языке запросов: ГДЕ Товары.Количество <> 0. Привычные по другим языкам != и ≠ платформа не понимает и выдаёт синтаксическую ошибку. Второй способ — отрицание через НЕ: Если НЕ (Сумма = 0) Тогда даёт тот же результат.
Типичная ситуация: программист переходит в 1С с C# или JavaScript, пишет в отборе запроса Статус != &Закрыт и получает ошибку разбора. Или хуже — пишет <> правильно, но в запросе с левым соединением половина строк «пропадает».
Оператор «не равно» и отрицание НЕ во встроенном языке
Во встроенном языке 1С есть шесть операторов сравнения. Все они возвращают значение типа Булево.
| Оператор | Смысл | Пример |
|---|---|---|
= | равно | Если Вид = "Услуга" Тогда |
<> | не равно | Если Остаток <> 0 Тогда |
< | меньше | Если Дата < НачалоГода Тогда |
> | больше | Если Сумма > Лимит Тогда |
<= | меньше или равно | Если Скидка <= 10 Тогда |
>= | больше или равно | Если Возраст >= 18 Тогда |
У логического отрицания два написания: русское НЕ и английское NOT. Регистр букв не важен: не, Не, НЕ работают одинаково.
// Три равносильные записи
Если Контрагент <> Справочники.Контрагенты.ПустаяСсылка() Тогда
Сообщить("Контрагент указан");
КонецЕсли;
Если НЕ Контрагент = Справочники.Контрагенты.ПустаяСсылка() Тогда
Сообщить("Контрагент указан");
КонецЕсли;
Если НЕ (Контрагент = Справочники.Контрагенты.ПустаяСсылка()) Тогда
Сообщить("Контрагент указан");
КонецЕсли;
Первая запись короче и читается быстрее. Отрицание НЕ удобнее, когда нужно перевернуть сложное условие целиком или проверить булеву переменную: Если НЕ Отказ Тогда.
💡 Для проверки «значение не пустое» часто удобнее функция ЗначениеЗаполнено(). Она возвращает Ложь для пустой ссылки, пустой строки, нуля, пустой даты и Неопределено. Запись Если ЗначениеЗаполнено(Контрагент) Тогда заменяет сравнение с ПустаяСсылка() и не зависит от типа реквизита.
Результат сравнения можно сразу записать в переменную, без Если:
ЕстьОстаток = (Остаток <> 0);
«Не равно» в запросах 1С: <>, НЕ В, НЕ ПОДОБНО
В языке запросов оператор тот же — <>. Он работает в ГДЕ, ИМЕЮЩИЕ, в условиях соединения и внутри ВЫБОР КОГДА.
ВЫБРАТЬ
Номенклатура.Ссылка,
Номенклатура.Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ВидНоменклатуры <> &ВидУслуга
И НЕ Номенклатура.ПометкаУдаления
И НЕ Номенклатура.ЭтоГруппа
Для отрицания других операций в запросе есть свои конструкции.
| Задача | Как записать |
|---|---|
| Не равно одному значению | Поле <> &Значение |
| Не входит в список | Поле НЕ В (&СписокЗначений) |
| Не входит в результат подзапроса | Поле НЕ В (ВЫБРАТЬ ... ИЗ ...) |
| Не входит в группу иерархии | НЕ Поле В ИЕРАРХИИ (&Группа) |
| Не содержит подстроку | НЕ Поле ПОДОБНО "%брак%" |
| Не попадает в диапазон | НЕ Поле МЕЖДУ &Начало И &Конец |
| Не NULL | НЕ Поле ЕСТЬ NULL |
Для В ИЕРАРХИИ, ПОДОБНО, МЕЖДУ и ЕСТЬ NULL надёжнее ставить НЕ перед всем выражением, как в таблице. Такая запись однозначна и одинаково читается любым разработчиком.
Сравнение с конкретным значением без параметра
Если сравниваете с предопределённым элементом, перечислением или пустой ссылкой, параметр не нужен. Используйте функцию ЗНАЧЕНИЕ():
ГДЕ
Документ.Организация <> ЗНАЧЕНИЕ(Справочник.Организации.ПустаяСсылка)
И Документ.ВидОперации <> ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализацияТоваров.Услуги)
Имена перечислений и значений здесь — пример. В вашей конфигурации посмотрите точное имя в дереве метаданных Конфигуратора.
Неравенство в условии соединения
<> допустим и в секции ПО. Пример: найти пары документов с одинаковым номером, но разными ссылками:
ВЫБРАТЬ
Д1.Ссылка КАК Первый,
Д2.Ссылка КАК Второй
ИЗ
Документ.СчетНаОплатуПокупателю КАК Д1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.СчетНаОплатуПокупателю КАК Д2
ПО Д1.Номер = Д2.Номер
И Д1.Ссылка <> Д2.Ссылка
Порядок вычисления: НЕ, И, ИЛИ и скобки
Ошибки в сложных условиях чаще всего связаны не с <>, а с приоритетом логических операций. В 1С порядок такой (от высшего к низшему):
- Выражение в скобках
( ). - Арифметика: сначала
*,/,%, затем+,-. - Операции сравнения:
=,<>,<,>,<=,>=. НЕ.И.ИЛИ.
Из этого следует, что НЕ Сумма = 0 читается как НЕ (Сумма = 0): сравнение выполняется раньше отрицания. Но запись со скобками понятнее коллеге, который будет править код через год.
Главная ловушка — смешение И и ИЛИ:
// Ошибка: условие читается как Статус <> Закрыт ИЛИ (Сумма > 0 И Проведен)
Если Статус <> Закрыт ИЛИ Сумма > 0 И Проведен Тогда
// Правильно: скобки задают задуманный порядок
Если (Статус <> Закрыт ИЛИ Сумма > 0) И Проведен Тогда
Отрицание составного условия
Чтобы перевернуть условие «А и Б», нельзя просто заменить каждое = на <>. Работают законы де Моргана:
| Исходное условие | Отрицание |
|---|---|
А = 1 И Б = 2 | А <> 1 ИЛИ Б <> 2 |
А = 1 ИЛИ Б = 2 | А <> 1 И Б <> 2 |
Поле = 1 ИЛИ Поле = 2 | Поле НЕ В (1, 2) в запросе |
Частая ошибка — написать Если Вид <> "Товар" ИЛИ Вид <> "Услуга" Тогда. Такое условие истинно всегда: любое значение не равно хотя бы одному из двух. Нужно И.
Сокращённое вычисление
Встроенный язык вычисляет логические выражения слева направо и останавливается, как только результат известен. На этом держится защитная проверка:
Если Объект <> Неопределено И Объект.Проведен Тогда
Если Объект равен Неопределено, вторая часть не выполняется, и ошибки обращения к полю не будет. Поменяете части местами — получите исключение.
Как 1С сравнивает строки, числа и даты
Оператор <> во встроенном языке можно применять к значениям разных типов. Ошибки не будет: число 5 и строка «5» просто не равны, результат — Истина.
Результат = (5 <> "5"); // Истина: разные типы
Результат = (5 <> 5.0); // Ложь: оба значения — числа
С операциями <, >, <=, >= так нельзя. Для значений разных примитивных типов они вызывают ошибку выполнения. Если число пришло строкой из файла или формы, сначала приведите тип: Число(СтрокаИзФайла) <> 0.
Строки
- 🔤 Во встроенном языке сравнение строк учитывает регистр: «Москва» и «москва» не равны. Для сравнения без учёта регистра приводите обе стороны:
ВРег(Город) <> ВРег("Москва"). - ✂️ Пробелы в начале и конце тоже участвуют в сравнении. Значения из файлов и внешних систем обрезайте через
СокрЛП(). - 🗄️ В запросе результат сравнения строк зависит от СУБД и её настроек сортировки. В файловой базе регистр обычно не учитывается. Если результат важен, не полагайтесь на это.
Строки неограниченной длины в запросе
Реквизиты типа «Строка» с неограниченной длиной (например, «Комментарий») нельзя напрямую сравнивать в запросе. Платформа выдаст ошибку о недопустимости сравнения полей неограниченной длины. Решение — ограничить длину в самом условии:
ГДЕ
ВЫРАЗИТЬ(Документ.Комментарий КАК СТРОКА(200)) <> ""
Или вынести проверку в ПОДСТРОКА(Документ.Комментарий, 1, 200).
Числа и даты
- 🔢 Числа в 1С десятичные, поэтому
0.1 + 0.2 <> 0.3вернётЛожь: сумма точно равна 0,3. Но после деления появляются длинные дроби. Результат расчёта цены или пропорции сравнивайте после округления:Окр(Сумма / Количество, 2) <> Цена. - 📅 Пустая дата во встроенном языке —
'00010101'илиДата(1, 1, 1). Проверка:Если ДатаОплаты <> Дата(1, 1, 1) Тогда. - 📅 В запросе пустую дату задают так:
ДАТАВРЕМЯ(1, 1, 1). Пример:ГДЕ Документ.ДатаОплаты <> ДАТАВРЕМЯ(1, 1, 1).
Даты в 1С хранятся с точностью до секунды. Условие ДатаДокумента <> &Дата, где параметр — начало дня, сработает почти для всех документов этого дня. Для сравнения «по дню» используйте НАЧАЛОПЕРИОДА(Поле, ДЕНЬ) <> &Дата в запросе или НачалоДня() в коде.
Перечисления, булевы значения и пустые ссылки
Перечисления
Во встроенном языке значение перечисления берут через менеджер Перечисления:
Если Документ.СтатусОплаты <> Перечисления.СтатусыОплаты.Оплачен Тогда
ДобавитьВСписокДолжников(Документ);
КонецЕсли;
В запросе — через ЗНАЧЕНИЕ(Перечисление.Имя.Значение) или через параметр. Сравнивать перечисление со строкой с его представлением нельзя: Статус <> "Оплачен" во встроенном языке всегда вернёт Истина, потому что типы разные. Ошибки не будет, и такую логику легко пропустить при отладке.
У незаполненного реквизита-перечисления значение — пустая ссылка. Проверка: Статус <> Перечисления.СтатусыОплаты.ПустаяСсылка() или ЗначениеЗаполнено(Статус).
Булево
Сравнивать булево с Истина или Ложь можно, но лишнее. Запись Если Проведен <> Истина Тогда равносильна Если НЕ Проведен Тогда, вторая короче.
Есть одно исключение. Если переменная может оказаться Неопределено (например, значение достали из структуры или из составного реквизита), то НЕ Переменная вызовет ошибку. Безопаснее явное сравнение: Если Флаг <> Истина Тогда — для Неопределено оно вернёт Истина без исключения.
Неопределено и NULL
Неопределено— значение встроенного языка. Проверка:Значение <> Неопределено.NULL— значение из результата запроса, когда в соединении не нашлось пары. Во встроенном языке проверяют так:Выборка.Цена <> NULL.
Где неравенство ломается: NULL, отборы и типичные ошибки
NULL после левого соединения
Самая частая проблема. Есть запрос по номенклатуре с левым соединением к ценам, условие ГДЕ Цены.Цена <> 100. Ожидается, что выберутся все товары, кроме тех, у которых цена ровно 100. На деле пропадают и товары без цены.
Причина: для них поле Цена равно NULL, а любое сравнение с NULL в запросе не даёт истины. Строка не проходит условие.
Два способа исправить:
// Способ 1: заменить NULL значением по умолчанию
ГДЕ
ЕСТЬNULL(Цены.Цена, 0) <> 100
// Способ 2: явно оставить строки без пары
ГДЕ
(Цены.Цена <> 100 ИЛИ Цены.Цена ЕСТЬ NULL)
Первый вариант короче. Второй точнее, если ноль — реальное значение цены.
Та же история с НЕ В: если в списке или подзапросе есть NULL, результат может отличаться от ожидаемого. Отбрасывайте NULL во вложенном запросе условием НЕ Поле ЕСТЬ NULL.
Поиск строк «не равно» в таблице значений
Методы НайтиСтроки() и Скопировать() с отбором ищут только по равенству. Отбора «не равно» у них нет. Варианты:
- Обойти таблицу циклом и проверить условие
<>в каждой строке. - Загрузить таблицу во временную таблицу запроса и отобрать через
ГДЕ Поле <> &Значение. Для больших таблиц это обычно быстрее.
То же ограничение у отбора наборов записей регистров: Отбор.Поле.Установить() работает только на равенство.
«Не равно» в отборах отчётов и динамических списков
В пользовательском режиме ничего писать не нужно. В отборе отчёта на системе компоновки или в настройке списка выберите вид сравнения «Не равно», «Не в списке» или «Не заполнено». В коде этот вид сравнения задаётся значением ВидСравненияКомпоновкиДанных.НеРавно:
ЭлементОтбора = Список.КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Статус");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.НеРавно;
ЭлементОтбора.ПравоеЗначение = Перечисления.СтатусыОплаты.Оплачен;
ЭлементОтбора.Использование = Истина;
Здесь Список — динамический список на форме, имя реквизита Статус — пример.
Частые вопросы
Как написать «не равно» на английском синтаксисе 1С?
<>. Отрицание пишется NOT, логические связки — AND и OR. Русский и английский варианты можно смешивать в одном модуле, но лучше держаться одного.Можно ли в запросе написать НЕ РАВНО словами?
<> или НЕ (Поле = &Значение).Чем `Поле <> &Значение` отличается от `НЕ Поле В (&Значение)` в запросе?
НЕ В нужен, когда в параметр передают список или массив значений: <> со списком не работает.Как проверить, что ссылка не пустая, в запросе?
Поле <> ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка). Если поле может прийти NULL из соединения, добавьте ЕСТЬNULL() или проверку ЕСТЬ NULL.Почему `Если Значение <> Неопределено` не ловит пустую строку?
Неопределено. Для проверки «есть ли что-то в переменной» используйте ЗначениеЗаполнено().