Почему строки с пробелами и без считаются равными в запросах 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с языком запросов 1С начинающие (а иногда и опытные) разработчики сталкиваются с неочевидным поведением системы: строки, которые визуально различаются наличием пробелов в конце, при сравнении в запросе ведут себя как идентичные. Это может привести к неожиданному увеличению количества строк в результате соединения таблиц или к некорректной фильтрации данных.

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

Анализ примера: почему соединение возвращает 4 строки?

Проанализируем исходный запрос, представленный в обсуждении. Сначала создается временная таблица Т, содержащая две строки: одну с символом "А" и вторую с символом "А" и пробелом после него.


ВЫБРАТЬ
    "А" КАК А
ПОМЕСТИТЬ Т
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
    "А "
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т.А,
    Т1.А КАК А1
ИЗ
    Т КАК Т
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Т КАК Т1
        ПО Т.А = Т1.А

На первый взгляд кажется, что результат должен содержать только 2 строки, где значения полностью совпадают. Однако запрос возвращает 4 строки. Выясним причину: это происходит потому, что для СУБД (и, следовательно, для платформы 1С) значение "А" равно значению "А ".

В результате внутреннего соединения каждая из двух строк первой таблицы находит соответствие в каждой из двух строк второй таблицы:

  1. "А" (из Т) соединяется с "А" (из Т1) — истина
  2. "А" (из Т) соединяется с "А " (из Т1) — истина
  3. "А " (из Т) соединяется с "А" (из Т1) — истина
  4. "А " (из Т) соединяется с "А " (из Т1) — истина

Итого получаем 2 * 2 = 4 комбинации. Это поведение не является ошибкой 1С, а проистекает из особенностей работы механизмов баз данных.

Стандарт ANSI SQL-92 и дополнение пробелами

Разберем фундаментальную причину. Согласно международному стандарту ANSI SQL-92, при сравнении двух строк разной длины с использованием оператора "равно" (=), более короткая строка логически дополняется пробелами справа до тех пор, пока ее длина не сравняется с длиной более длинной строки. Только после этого происходит посимвольное сравнение.

Таким образом, когда СУБД видит условие "А" = "А ", она фактически выполняет сравнение "А " = "А ". Именно поэтому результат оказывается положительным. Это правило реализовано в большинстве современных СУБД, таких как MS SQL Server и Oracle, для обеспечения предсказуемости и совместимости стандартов.

Важное замечание: это правило касается только пробелов в конце строки (trailing spaces). Пробелы в начале строки (leading spaces) учитываются всегда. Так, сравнение "А" = " А" вернет Ложь, так как первый символ строки будет различаться.

Особенности различных СУБД и файловой базы

Проанализируем, как это работает на разных платформах. В MS SQL Server тип данных nvarchar, который 1С использует для хранения строк, всегда следует этому правилу. В PostgreSQL поведение типов text или varchar может быть более строгим, однако платформа 1С принудительно эмулирует поведение MS SQL Server для обеспечения идентичности работы запросов во всех вариантах поставки (файловом и клиент-серверном).

Как было отмечено в обсуждении на форуме, в файловой базе данных результат будет точно таким же — 4 строки. Это подтверждает, что логика сравнения строк зашита глубоко в ядро платформы 1С для сохранения единообразия разработки.

Влияние параметров сортировки (Collation)

Посмотрим на ситуацию с точки зрения регистра символов. Часто возникает вопрос: будут ли равны "а" и "А"? Это зависит от настроек Collation (параметров сортировки) базы данных. 1С по умолчанию устанавливает параметры _CI_AS (Case Insensitive, Accent Sensitive). Это означает:

Следовательно, в стандартной базе 1С строки "а", "А" и "А " будут считаться равными при использовании оператора =.

Как реализовать точное сравнение (проверка на "истинное" равенство)

Иногда бизнес-логика требует отличать строки с пробелами от строк без них. Рассмотрим два основных способа решения этой задачи в запросе.

Способ 1: Использование оператора ПОДОБНО (LIKE)

Оператор ПОДОБНО в языке запросов 1С ведет себя иначе, чем простое равенство. Он учитывает концевые пробелы. Если мы заменим условие соединения или фильтрации, результат изменится.

Рассмотрим пример:

ГДЕ Т.А ПОДОБНО "А"

В этом случае в результат попадет только строка, в которой нет лишних символов. Однако использование ПОДОБНО в условиях соединения (ПО Т.А ПОДОБНО Т1.А) часто невозможно или крайне неэффективно с точки зрения производительности.

Способ 2: Искусственное изменение длины строки (Конкатенация)

Это самый надежный и распространенный "лайфхак" среди разработчиков 1С. Чтобы заставить СУБД учитывать концевой пробел, мы должны сделать так, чтобы этот пробел перестал быть "концевым". Для этого к обеим сравниваемым строкам добавляется какой-либо символ-заполнитель (например, нижнее подчеркивание).

Посмотрим, как изменится наше условие соединения:


ВЫБРАТЬ
    Т.А,
    Т1.А КАК А1
ИЗ
    Т КАК Т
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Т КАК Т1
        ПО Т.А + "_" = Т1.А + "_"

Проанализируем, что произойдет при сравнении "А" и "А ":

  1. Первая строка превращается в "А_".
  2. Вторая строка превращается в "А _".
  3. Теперь СУБД сравнивает эти строки посимвольно: вторым символом в первой строке является "_", а во второй — пробел.
  4. Результат сравнения — Ложь.

При использовании такого подхода наш исходный запрос вернет ровно 2 строки, что и требовалось для точного соответствия.

Почему функции длины строки не всегда помогают?

Разберем еще один нюанс. Может показаться, что проблему можно решить проверкой длины строки. Но здесь нас подстерегает ловушка: встроенная в SQL Server функция LEN() также игнорирует концевые пробелы при подсчете. То есть LEN("А") и LEN("А ") вернут одно и то же число — 1.

В самом языке запросов 1С функция СТРДЛИНА (внутри SQL-запроса) транслируется именно в LEN(), поэтому она не поможет отличить эти строки. Существует функция DATALENGTH(), которая учитывает все символы, но в стандартном языке запросов 1С прямого аналога этой функции нет.

Резюме

Выяснив причины поведения системы, можно сделать следующие выводы:

← На главную