При работе с языком запросов 1С начинающие (а иногда и опытные) разработчики сталкиваются с неочевидным поведением системы: строки, которые визуально различаются наличием пробелов в конце, при сравнении в запросе ведут себя как идентичные. Это может привести к неожиданному увеличению количества строк в результате соединения таблиц или к некорректной фильтрации данных.
Рассмотрим ситуацию на конкретном примере, разберем причины такого поведения СУБД и выясним, как добиться строгого соответствия строк, если это необходимо для логики программы.
Проанализируем исходный запрос, представленный в обсуждении. Сначала создается временная таблица Т, содержащая две строки: одну с символом "А" и вторую с символом "А" и пробелом после него.
ВЫБРАТЬ
"А" КАК А
ПОМЕСТИТЬ Т
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"А "
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т.А,
Т1.А КАК А1
ИЗ
Т КАК Т
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Т КАК Т1
ПО Т.А = Т1.А
На первый взгляд кажется, что результат должен содержать только 2 строки, где значения полностью совпадают. Однако запрос возвращает 4 строки. Выясним причину: это происходит потому, что для СУБД (и, следовательно, для платформы 1С) значение "А" равно значению "А ".
В результате внутреннего соединения каждая из двух строк первой таблицы находит соответствие в каждой из двух строк второй таблицы:
Итого получаем 2 * 2 = 4 комбинации. Это поведение не является ошибкой 1С, а проистекает из особенностей работы механизмов баз данных.
Разберем фундаментальную причину. Согласно международному стандарту 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 (параметров сортировки) базы данных. 1С по умолчанию устанавливает параметры _CI_AS (Case Insensitive, Accent Sensitive). Это означает:
_CI — регистронезависимость: "А" равно "а"._AS — чувствительность к диакритическим знакам (ударениям, точкам над ё).Следовательно, в стандартной базе 1С строки "а", "А" и "А " будут считаться равными при использовании оператора =.
Иногда бизнес-логика требует отличать строки с пробелами от строк без них. Рассмотрим два основных способа решения этой задачи в запросе.
Оператор ПОДОБНО в языке запросов 1С ведет себя иначе, чем простое равенство. Он учитывает концевые пробелы. Если мы заменим условие соединения или фильтрации, результат изменится.
Рассмотрим пример:
ГДЕ Т.А ПОДОБНО "А"
В этом случае в результат попадет только строка, в которой нет лишних символов. Однако использование ПОДОБНО в условиях соединения (ПО Т.А ПОДОБНО Т1.А) часто невозможно или крайне неэффективно с точки зрения производительности.
Это самый надежный и распространенный "лайфхак" среди разработчиков 1С. Чтобы заставить СУБД учитывать концевой пробел, мы должны сделать так, чтобы этот пробел перестал быть "концевым". Для этого к обеим сравниваемым строкам добавляется какой-либо символ-заполнитель (например, нижнее подчеркивание).
Посмотрим, как изменится наше условие соединения:
ВЫБРАТЬ
Т.А,
Т1.А КАК А1
ИЗ
Т КАК Т
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Т КАК Т1
ПО Т.А + "_" = Т1.А + "_"
Проанализируем, что произойдет при сравнении "А" и "А ":
"А_"."А _"."_", а во второй — пробел.При использовании такого подхода наш исходный запрос вернет ровно 2 строки, что и требовалось для точного соответствия.
Разберем еще один нюанс. Может показаться, что проблему можно решить проверкой длины строки. Но здесь нас подстерегает ловушка: встроенная в SQL Server функция LEN() также игнорирует концевые пробелы при подсчете. То есть LEN("А") и LEN("А ") вернут одно и то же число — 1.
В самом языке запросов 1С функция СТРДЛИНА (внутри SQL-запроса) транслируется именно в LEN(), поэтому она не поможет отличить эти строки. Существует функция DATALENGTH(), которая учитывает все символы, но в стандартном языке запросов 1С прямого аналога этой функции нет.
Выяснив причины поведения системы, можно сделать следующие выводы:
= в запросах 1С всегда игнорирует концевые пробелы из-за стандартов SQL.+ "_") или оператор ПОДОБНО.