sql-server — SQL Server 2008 Пустая строка против пространства" />

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

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

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

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

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

select (LEN(' '))

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

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

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

76 голосов | спросил jhale 9 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 09 Sep 2009 17:56:05 +0400 2009, 17:56:05

6 ответов


0

varchar и равенство в TSQL непросто. Функция LEN сообщает:

  

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

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

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
ответил butterchicken 9 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 09 Sep 2009 18:14:21 +0400 2009, 18:14:21
0

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

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

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

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

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

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

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

  

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

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

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

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

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

ответил Steve Kass 9 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 09 Sep 2009 19:20:55 +0400 2009, 19:20:55
0

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

  

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

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

ответил JohnFx 9 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 09 Sep 2009 19:03:21 +0400 2009, 19:03:21
0

Некоторое время назад был похожий вопрос, в котором я рассмотрел аналогичную проблему -match-и-строки, которые матча /1143465 # 1143465" > здесь

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

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

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

ответил AdaTheDev 9 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 09 Sep 2009 18:12:25 +0400 2009, 18:12:25
0

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

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'
ответил David G 24 FebruaryEurope/MoscowbThu, 24 Feb 2011 23:03:11 +0300000000pmThu, 24 Feb 2011 23:03:11 +030011 2011, 23:03:11
0

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

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

ожидается

mykey (int) | 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)

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

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

ответил Orix 14 PMpTue, 14 Apr 2015 18:45:59 +030045Tuesday 2015, 18:45:59

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132