Как работает «=» в коде, запросах и настройках отчётов 1С

· 10 мин

Содержание

Во встроенном языке 1С знак равно делает две разные вещи: в начале оператора он присваивает значение (Сумма = 100;), а внутри выражения сравнивает (Если Сумма = 100 Тогда). Отдельного оператора ==, как в C или JavaScript, нет. В языке запросов и в отборах отчётов «=» означает только сравнение.

Путаница начинается на стыке: строка не равна сама себе из-за пробела, ссылка никогда не равна названию контрагента, а условие Поле = NULL в запросе не находит ни одной записи. Все эти случаи объясняются тем, что «=» сравнивает значение вместе с его типом.

Присваивание: «=» в начале оператора

Если строка кода начинается с переменной, реквизита или элемента коллекции, за которым идёт «=», платформа читает это как присваивание. Всё, что справа, вычисляется и записывается в то, что слева.

Сумма = 1500;
Объект.Комментарий = "Создан обработкой";
Структура.Вставить("Ставка", 20);
Структура.Ставка = 22;
ТаблицаТоваров[0].Количество = 3;

Что важно знать про присваивание:

  • 📌 Переменную объявлять не обязательно. Первое присваивание создаёт локальную переменную. Перем нужен для переменных модуля и для читаемости.
  • 🔁 Цепочки не работают как в C. А = Б = 5; не запишет пятёрку в обе переменные. Первый знак — присваивание, второй — сравнение: в А попадёт Истина или Ложь в зависимости от того, равно ли Б пяти.
  • 📦 Объекты присваиваются по ссылке. После Массив2 = Массив1; обе переменные указывают на один и тот же массив. Добавили элемент в Массив2 — он появился и в Массив1.

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

Для таблиц значений есть метод Скопировать(): НоваяТаблица = Таблица.Скопировать();. Он создаёт отдельную таблицу, а не вторую ссылку на старую.

Сравнение в условиях: «=» внутри выражения

Везде, где ожидается логическое значение, «=» сравнивает два операнда и возвращает Истина или Ложь. Это условия Если, ИначеЕсли, Пока, тернарный оператор ?(), правая часть присваивания.

Если Документ.Проведен = Истина Тогда
    // ...
КонецЕсли;

Пока Остаток = 0 Цикл
    // ...
КонецЦикла;

Текст = ?(Количество = 1, "товар", "товаров");
ЭтоРубли = (Валюта = Константы.ВалютаРегламентированногоУчета.Получить());

Последняя строка показывает оба смысла сразу: первое «=» присваивает, второе в скобках сравнивает. Скобки здесь не обязательны, но с ними код читается без усилий.

Полный набор операций сравнения:

ОперацияЗначениеПример
=равноКод = "000001"
<>не равноСтатус <> Перечисления.СтатусыЗаказов.Закрыт
<, >меньше, большеСумма > 0
<=, >=меньше или равно, больше или равноДата >= НачалоМесяца(ТекущаяДата())

Знака != в языке нет, только <>. Отрицание через НЕ тоже возможно, но Если НЕ (А = Б) проще записать как Если А <> Б.

Чтобы не гадать о приоритете НЕ и операций сравнения, всегда берите сравнение в скобки: НЕ (Сумма = 0). Без скобок такие условия легко прочитать неправильно при ревью.

Про булевы реквизиты. Если Флаг = Истина Тогда и Если Флаг Тогда дают одно и то же, когда в Флаг лежит булево. Разница появляется, если значение может быть Неопределено: короткая запись упадёт с ошибкой преобразования к булеву, а сравнение с Истина просто вернёт Ложь.

Как «=» сравнивает разные типы данных

Именно здесь чаще всего рождаются ошибки. Правило одно: «=» сравнивает значения с учётом типа. Если типы разные, результат — Ложь, без исключения.

Сообщить(1 = "1");          // Ложь: число и строка
Сообщить("1" = Строка(1));  // Истина
Сообщить(Число("1") = 1);   // Истина

С операциями < и > так не выйдет: для значений разных типов платформа выдаёт ошибку выполнения. Равенство же молча возвращает Ложь, и ошибка в логике может жить годами.

Строки

  • 🔠 Регистр учитывается. "Ромашка" = "ромашка" даёт Ложь.
  • 📏 Пробелы учитываются. "ООО Ромашка" = "ООО Ромашка " даёт Ложь. Хвостовые пробелы часто приходят из загрузки Excel или из реквизитов фиксированной длины.

Как сравнить «по-человечески»:

// Без учёта регистра
Если ВРег(СокрЛП(Стр1)) = ВРег(СокрЛП(Стр2)) Тогда
    // ...
КонецЕсли;

// Функция СтрСравнить не учитывает регистр, 0 означает равенство
Если СтрСравнить(СокрЛП(Стр1), СокрЛП(Стр2)) = 0 Тогда
    // ...
КонецЕсли;

Числа

Тип «Число» в 1С десятичный, поэтому классической проблемы двоичной дроби нет: 0.1 + 0.2 = 0.3 возвращает Истина. Но результат деления хранится с ограниченной точностью, а реквизиты обрезаются по своей разрядности при записи.

Поэтому вычисленную сумму перед сравнением с суммой из базы округляйте до той же точности:

Если Окр(Цена * Количество, 2) = СтрокаТЧ.Сумма Тогда
    // суммы совпадают с точностью до копейки
КонецЕсли;

Даты

Дата в 1С всегда содержит время. '20261007' = '20261007153000' даёт Ложь: у второго значения есть 15:30:00. Сравнить «один и тот же день» можно так:

Если НачалоДня(Документ.Дата) = НачалоДня(ТекущаяДатаСеанса()) Тогда
    // документ сегодняшний
КонецЕсли;

Ссылки, объекты и коллекции

Что сравниваемКогда «=» вернёт Истина
Ссылки (СправочникСсылка, ДокументСсылка)Обе указывают на один и тот же элемент базы
Ссылка и строка с наименованиемНикогда — разные типы
Пустая ссылкаСсылка = Справочники.Контрагенты.ПустаяСсылка() или Ссылка.Пустая()
Значения перечисленияВид = Перечисления.ВидыКонтрагентов.ЮрЛицо
Объекты, массивы, структурыТолько если это один и тот же экземпляр в памяти
Тип значенияТипЗнч(Значение) = Тип("Строка")

Два массива с одинаковыми элементами не равны друг другу. Две структуры с одинаковыми ключами и значениями — тоже. Сравнивать их приходится поэлементно.

Неопределено и Null

Во встроенном языке Неопределено = Неопределено и Null = Null дают Истина, а Неопределено = Null — Ложь. Это частая ловушка при разборе результата запроса: незаполненное поле из левого соединения приходит как Null, а не как Неопределено и не как пустая строка.

Если Выборка.Цена = Null Тогда
    Цена = 0;
КонецЕсли;

Короче то же самое делает функция ЗначениеЗаполнено(): она вернёт Ложь для Null, Неопределено, пустой строки, нуля, пустой даты и пустой ссылки.

Знак равно в языке запросов 1С

В тексте запроса «=» никогда не присваивает. Он работает в условиях ГДЕ, ИМЕЮЩИЕ, в связях соединений ПО и в выражениях ВЫБОР КОГДА.

ВЫБРАТЬ
    Продажи.Номенклатура,
    ВЫБОР
        КОГДА Продажи.Количество = 0 ТОГДА "Без количества"
        ИНАЧЕ "Обычная строка"
    КОНЕЦ КАК Признак
ИЗ
    РегистрНакопления.Продажи КАК Продажи
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
        ПО Продажи.Номенклатура = Номенклатура.Ссылка
ГДЕ
    Продажи.Контрагент = &Контрагент
    И Продажи.Период >= &НачалоПериода

Отличия от встроенного языка, которые нужно держать в голове:

  1. NULL сравнивают только через ЕСТЬ NULL. Условие Поле = NULL не вернёт ни одной строки, даже там, где значение действительно пустое. Правильно: ГДЕ Цены.Цена ЕСТЬ NULL или ГДЕ НЕ Цены.Цена ЕСТЬ NULL.
  2. Предопределённые значения и пустые ссылки — через ЗНАЧЕНИЕ(). Пример: ГДЕ Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт) или Поле = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).
  3. Для списка значений нужен В, а не «=». Если в параметр передан массив или список значений, пишите ГДЕ Товары.Номенклатура В (&СписокТоваров).
  4. Строки неограниченной длины напрямую не сравнить. Реквизит-строка без ограничения длины (типично «Комментарий») в условии с «=» даёт ошибку запроса. Обрежьте его: ПОДСТРОКА(Документ.Комментарий, 1, 200) = &Текст.
  5. Составной тип проверяют через ССЫЛКА. ГДЕ Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг отбирает строки нужного типа, а «=» проверяет уже конкретное значение.

Регистр букв при сравнении строк в запросе обычно не учитывается, а хвостовые пробелы отбрасываются. Это поведение зависит от СУБД и её настроек, поэтому не опирайтесь на него в критичной логике — проверьте на своей базе.

Значения в запрос передавайте параметрами (&Контрагент), а не склейкой строки. Так не будет проблем с типами, кавычками и форматом даты.

Вид сравнения «Равно» в отборах списков и отчётов

В пользовательском режиме знак равно превращается в вид сравнения «Равно». Он есть в настройках отбора динамических списков, в отчётах на системе компоновки данных (СКД) и в отборах регистров.

Как задать отбор «Равно» руками:

  1. В списке откройте меню Ещё и выберите настройку списка, вкладку Отбор. В отчёте нажмите кнопку Настройки и перейдите к отборам.
  2. Добавьте поле, например «Контрагент».
  3. В колонке вида сравнения выберите Равно.
  4. Укажите значение и включите флажок использования.

Чем «Равно» отличается от соседних видов сравнения:

Вид сравненияЧто отберётКогда выбирать
РавноРовно одно значениеКонкретный контрагент, склад, организация
Не равноВсё, кроме значенияИсключить одну организацию
В спискеЛюбое из нескольких значенийТри склада из десяти
В группеЭлемент и всех подчинённыхГруппа номенклатуры со всеми вложенными
СодержитПодстроку в строкеПоиск по части наименования

Для иерархических справочников «Равно» по группе не захватывает её элементы. Отбор «Номенклатура Равно Молочная продукция» покажет только строки, где выбрана сама группа, то есть обычно ничего. Нужен вид «В группе».

Отбор «Равно» из кода

Динамический список:

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

Набор записей регистра: метод Установить() у элемента отбора сам ставит сравнение на равенство.

НаборЗаписей = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Номенклатура.Установить(Номенклатура);
НаборЗаписей.Прочитать();

Поиск в таблице значений по равенству полей:

Отбор = Новый Структура("Склад, Номенклатура", Склад, Номенклатура);
НайденныеСтроки = ТаблицаОстатков.НайтиСтроки(Отбор);

НайтиСтроки сравнивает поля по тем же правилам, что и «=»: с учётом типа, регистра и пробелов.

В вычисляемых полях и выражениях СКД синтаксис ближе к языку запросов: ВЫБОР КОГДА Сумма = 0 ТОГДА "—" ИНАЧЕ Сумма КОНЕЦ.

Проверьте себя

Проверьте себя
Что окажется в переменной А после строки А = Б = 5; если Б равно 5?
Что вернёт выражение 1 = "1" во встроенном языке?
Как в запросе отобрать строки, где цена не заполнена после левого соединения?
Какой вид сравнения отберёт группу номенклатуры вместе с вложенными элементами?

Ошибки с оператором равно и как их найти

Диагностика: выберите симптом

Ещё одна тихая ошибка — сравнение суммы документа с вычисленным значением без округления. Расхождение в долях копейки даёт Ложь, и проверка срабатывает там, где визуально всё сходится. Округляйте вычисление до разрядности реквизита, как в примере выше.

Чтобы быстро понять, что именно лежит в переменной, остановитесь в отладчике на строке сравнения и посмотрите значение и его тип в табло. Тип часто объясняет всё сразу: вместо ожидаемой ссылки там строка, вместо даты — Неопределено.

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

Можно ли в одном условии и присвоить, и сравнить, как в C?
Нет. Внутри условия «=» всегда сравнивает. Строка Если А = 5 Тогда ничего не записывает в А. Присваивание возможно только отдельным оператором.
Чем отличается = от <> и НЕ?
= проверяет равенство, <> проверяет неравенство. НЕ (А = Б) даёт тот же результат, что А <> Б, но запись длиннее и требует скобок.
Почему Null = Null в коде даёт Истина, а в запросе нет?
Во встроенном языке Null — обычное значение, равное самому себе. В запросе действует логика SQL: сравнение с NULL не даёт истины, поэтому там нужен ЕСТЬ NULL.
Как проверить, что значение одного из нескольких типов?
Сравните тип: ТипЗнч(Значение) = Тип(“СправочникСсылка.Контрагенты”). Для нескольких вариантов создайте ОписаниеТипов и вызовите его метод СодержитТип().
Учитывает ли отбор «Равно» в отчёте регистр букв?
Для ссылок вопрос не стоит: сравниваются элементы базы, а не текст. Для строковых полей отбор выполняется запросом, поэтому поведение такое же, как у «=» в языке запросов.