Вопрос

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

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

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

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

select (LEN(' '))

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

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

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

Это было полезно?

Решение

varchars и равенство являются сложными в 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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ознакомьтесь с этим вопросом и ссылками там.

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

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

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

...где ('>' + @пробел + '<') <> ('>' + @пробел2 + '<')

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

Герберт

Как различать записи при выборе с помощью полей char / varchar на sql server:пример:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

ожидаемый

mykey (int) | мое поле (varchar10)

1 | " данные "

полученный

мой ключ | 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 есть индекс, он будет использоваться в каждом конкретном случае.

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

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

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

ВОЗВРАТ:не равны

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top