2009-09-09 6 views
72

Сегодня утром я столкнулся с чем-то странным и подумал, что отправлю его для комментариев.SQL Server 2008 Empty String vs. Space

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

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

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

select (LEN(' ')) 

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

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

У кого-нибудь есть информация по этому вопросу?

+2

SQL 2005: select len ​​('') возвращает 0 – Mayo

+1

Он делает то же самое на Sql Server 2000. –

+1

Это интересный вопрос. Кажется, что он возвращается равным независимо от того, сколько пробелов вы помещаете в любую строку, независимо от того, совпадают они или нет. После дополнительных экспериментов я заметил, что он эффективно выполняет RTRIM с обеих сторон оператора равенства перед сравнением. Похоже, что у вас есть ответ на функцию LEN, но мне действительно интересен более тщательный ответ, чем «varchars and equal are trorny in TSQ» для части равенства вашего вопроса. – JohnFx

ответ

74

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

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

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

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

Когда речь идет о равенстве выражений, две строки сравниваются равенства, как это:

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

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

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

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

Даст eq время:

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

Отослать ne

Осторожно с LIKE: это не симметричный: он рассматривает конечные пробелы как значимые в шаблоне (RHS), но не выражение соответствия (LHS). Далее берется из here:

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 
+1

Хороший ответ. Я не заметил этого в документации LEN. Однако это не ограничивается LEN. Функция RIGHT и LEFT демонстрирует подобное поведение, но там она не документирована. Кажется, это буквальный пробел, который вызывает проблему. Я заметил это также возвращается равным: , если «» = КОСМОС (1) \t печати «равно» еще \t печати «не равно» Я на самом деле не заинтересованы в получении истинной длины, я просто запутался, почему когда я искал пробел в столбце, все столбцы, которые были пустыми, были возвращены. – jhale

+0

Также, хорошая информация о заявлении LIKE.Я думаю, что мораль этой истории пытается не попасть в положение, когда вам нужно сравнить пространство и пустую строку. – jhale

+2

Проблема больше, чем сравнение пробела с пустой строкой. Сравнение любых двух строк, которые заканчиваются в другом количестве пространств, демонстрирует одно и то же поведение. – JohnFx

4

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

Вместо LEN («„), используйте DATALENGTH (“») - что дает правильное значение.

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

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

9

Я нашел blog article, которая описывает поведение и объясняет, почему.

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

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

+0

Спасибо. Я удивлен, что это стандарт. Я уверен, что кто-то гораздо умнее, чем у меня есть веская причина для этого. – jhale

+0

@John: Вы хотели написать ≠ (не равно) в своем комментарии? –

+0

Исходная цитата имела ошибку в ней, которую я скопировал напрямую. Я обновил цитату, чтобы отразить то, что имел в виду первоначальный автор. – JohnFx

14

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

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

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

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

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

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

См Using char and varchar Data в Books Online. Цитируя с этой страницы, упор мой:

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

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

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

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

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

+0

BOL описывает оператор = следующим образом: «Сравнивает равенство двух выражений (оператор сравнения)». Правильно ли это поведение или нет, вы должны признать, что он чрезвычайно запутан и нестандартен с точки зрения использования этого оператора в большинстве языков программирования. MS должна хотя бы добавить предупреждение в документацию об этом поведении. – JohnFx

+0

@JohnFx: См. Мой ответ слишком долго для комментариев в моем ответе. –

3

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

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

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

... где ('>' + @space + '< ') <> ('>' + @ пространство2 + '<')

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

Герберт

+1

Вопрос был _why_ SQL-сервер вел себя так, как он, а не как справиться с таким поведением в целом. jhale скорее всего не изменит код программы, а только его конфигурацию сервера. –

0

Как отчетливый записи на выбор с полями голец/VARCHAR на SQL Server: пример:

declare @mayvar as varchar(10) 

set @mayvar = 'data ' 

select mykey, myfield from mytable where myfield = @mayvar 

ожидается

MyKey (интермедиат) | myfield (varchar10)

1 | 'Данные'

получают

MyKey | myfield

1 | 'данные' 2 | «Данные»

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

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

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

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

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

Смежные вопросы