Пустая строка SQL Server 2008 против пространства

Сегодня утром я столкнулся с чем-то странным и подумал, что отправлю его для комментариев.

Может кто-нибудь объяснить, почему следующий SQL-запрос печатает "равный" при запуске с SQL 2008. Уровень совместимости db равен 100.

if '' = ' '
    print 'equal'
else
    print 'not equal'

И это возвращает 0:

select (LEN(' '))

Кажется, это автоматическая обрезка пробела. Я не знаю, было ли это в предыдущих версиях SQL Server, и мне больше не нужно проверять его.

Я столкнулся с этим, потому что производственный запрос возвращал неверные результаты. Я не могу найти это поведение в любом месте.

Есть ли у кого-нибудь информация об этом?

Ответ 1

varchar и равенство тернимы в TSQL. Функция LEN говорит:

Возвращает количество символов, а не количество байтов данного строкового выражения, исключая конечные пробелы.

Вам нужно использовать DATALENGTH для получения истинного byte количества данных. Если у вас есть данные в Юникоде, обратите внимание, что значение, которое вы получаете в этой ситуации, будет не таким, как длина текста.

print(DATALENGTH(' ')) --1
print(LEN(' '))        --0

Когда дело доходит до равенства выражений, две строки сравниваются для равенства следующим образом:

  • Получить более короткую строку
  • Папка с пробелами, пока длина не будет равна длине строки
  • Сравните два

Это средний шаг, который вызывает неожиданные результаты - после этого шага вы эффективно сравниваете пробелы с пробелами - следовательно, они считаются равными.

LIKE ведет себя лучше, чем = в ситуации "пробелов", потому что он не выполняет пустую прописку по шаблону, который вы пытались сопоставить:

if '' = ' '
print 'eq'
else
print 'ne'

Дает eq, а:

if '' LIKE ' '
print 'eq'
else
print 'ne'

Дает ne

Осторожно с LIKE, хотя: он не симметричен: он рассматривает конечные пробелы как значимые в шаблоне (RHS), но не выражение соответствия (LHS). Ниже приведено здесь:

declare @Space nvarchar(10)
declare @Space2 nvarchar(10)

set @Space = ''
set @Space2 = ' '

if @Space like @Space2
print '@Space Like @Space2'
else
print '@Space Not Like @Space2'

if @Space2 like @Space
print '@Space2 Like @Space'
else
print '@Space2 Not Like @Space'

@Space Not Like @Space2
@Space2 Like @Space

Ответ 2

Оператор = это T-SQL не столько "равно", сколько и есть "одно и то же слово/фраза, в соответствии с сортировкой контекста выражения", а LEN - "количество символов в слове/фраза." Никакие сортировки не обрабатывают завершающие пробелы как часть предшествующего им слова/фразы (хотя они обрабатывают ведущие пробелы как часть строки, которой они предшествуют).

Если вам нужно отличить 'this' от "this", вы не должны использовать оператор "те же слова или фраза", потому что 'this' и "это" - это одно и то же слово.

Вклад в работу = означает, что оператор равенства по строкам должен зависеть от содержимого своих аргументов и контекста сопоставления выражения, но он не должен зависеть от типов аргументов, если они оба типа строк.

Концепция естественного языка "это одно и то же слово", как правило, недостаточно точная, чтобы быть захваченным математическим оператором типа =, и нет понятия строкового типа на естественном языке. Контекст (т.е. Сопоставление) имеет значение (и существует на естественном языке) и является частью истории, а дополнительные свойства (некоторые из которых кажутся причудливыми) являются частью определения =, чтобы сделать его четко определенным в неестественном мире данных.

В вопросе типа вы не хотите, чтобы слова менялись, когда они хранятся в разных строковых типах. Например, типы VARCHAR (10), CHAR (10) и CHAR (3) могут содержать представления слова "cat" и? = 'cat' должен позволить нам решить, имеет ли значение любого из этих типов слово "кот" (с вопросами случая и акцента, определяемыми путем сопоставления).

Ответ на комментарий JohnFx:

См. Использование char и данных varchar в электронной документации. Цитата из этой страницы, мой удар:

Каждое значение char и varchar имеет сортировку. Обозначения атрибуты, такие как битовые шаблоны, используемые для представления каждого символа, правила сравнения, а также чувствительность к случаю или акценту.

Я согласен, что это может быть проще найти, но оно задокументировано.

Стоит также отметить, что семантика SQL, где = имеет отношение к реальным данным, и контекст сравнения (в отличие от чего-то о битах, хранящихся на компьютере) был частью SQL для долгого время. Предпосылка РСУБД и SQL - это точное представление реальных данных, поэтому поддержка коллабораций за многие годы до того, как подобные идеи (такие как CultureInfo) вошли в царство алгол-подобных языков. Посылка этих языков (по крайней мере до недавнего времени) была решением проблем в области разработки, а не для управления бизнес-данными. (В последнее время использование подобных языков в нетехнических приложениях, таких как поиск, делает некоторые набеги, но Java, С# и т.д. Все еще борется с их некоммерческими корнями.)

На мой взгляд, нечестно критиковать SQL за то, что он отличается от "большинства языков программирования". SQL был разработан для поддержки структуры моделирования бизнес-данных, которая очень отличается от разработки, поэтому язык отличается (и лучше для его цели).

Черт, когда SQL был указан первым, некоторые языки не имели встроенного строкового типа. И на некоторых языках оператор equals между строками вообще не сравнивает характерные данные, но сравнивает ссылки! Меня не удивило бы, если через какое-то десятилетие или две, идея, что == зависит от культуры, становится нормой.

Ответ 3

Я нашел эту статью статью в блоге, в которой описывается поведение и объясняется, почему.

Стандарт SQL требует, чтобы строка сравнений, эффективно, более короткая строка с пробелами.Это приводит к неожиданному результату что N '' = N '' (пустая строка равно одной или нескольким пробелам символов), и в целом любой строка равна другой строке, если они отличаются только конечными пробелами. Эта может быть проблемой в некоторых контекстах.

Дополнительная информация также доступна в MSKB316626

Ответ 4

Был аналогичный вопрос некоторое время назад, когда я рассмотрел аналогичную проблему здесь

Вместо LEN ('') используйте DATALENGTH ('') - это дает правильное значение.

Решения должны были использовать предложение LIKE, как описано в моем ответе, и/или включить в условие WHERE второе условие, чтобы проверить DATALENGTH тоже.

Прочитайте этот вопрос и ссылки там.

Ответ 5

Чтобы сравнить значение с буквальным пространством, вы также можете использовать этот метод в качестве альтернативы выражению LIKE:

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'

Ответ 6

Иногда приходится иметь дело с пробелами в данных, с или без каких-либо других символов, даже если идея использования Null лучше - но не всегда применима. Я столкнулся с описанной ситуацией и решил ее так:

... где (' > ' + @space + '<') < > (' > ' + @space2 + '<')

Конечно, вы не сделали бы этого большого количества данных fpr, но он работает быстро и легко для нескольких сотен строк...

Герберт

Ответ 7

Как отличать записи по выбору с полями char/varchar на сервере sql: Пример:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

ожидается

mykey (int) | myfield (varchar10)

1 | 'data'

получается

mykey | MyField

1 | 'данные' 2 | 'data'

даже если я пишу select mykey, myfield from mytable where myfield = 'data' (без окончательного пробела) Я получаю те же результаты.

как я решил? В этом режиме:

select mykey, myfield
from mytable
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)

и если в моем поле есть индекс, он будет использоваться в каждом случае.

Я надеюсь, что это будет полезно.

Ответ 8

Другой способ - вернуть его в состояние, в котором пространство имеет ценность. Например: заменить пробел на символ, известный как _

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

возвращает: не равно

Не идеально и, вероятно, медленно, но это еще один быстрый путь вперед, когда это необходимо быстро.