Сегодня утром я наткнулся на что-то немного странное и подумал, что отправлю это для комментария.
Может ли кто-нибудь объяснить, почему следующий запрос SQL печатает «равно» при запуске с SQL 2008. Уровень совместимости db установлен на 100.
if '' = ' '
print 'equal'
else
print 'not equal'
И это возвращает 0:
select (LEN(' '))
Кажется, это автоматическая обрезка пространства. Я понятия не имею, было ли это в предыдущих версиях SQL Server, и у меня больше нет возможности даже проверить это.
Я столкнулся с этим, потому что производственный запрос возвращал неверные результаты. Я нигде не могу найти документированного такого поведения
У кого-нибудь есть информация по этому поводу?
Ответы:
varchar
s и равенство тернисты в TSQL.LEN
Функция говорит:Вам нужно использовать,
DATALENGTH
чтобы получить истинноеbyte
количество рассматриваемых данных. Если у вас есть данные в Юникоде, обратите внимание, что значение, которое вы получите в этой ситуации, не будет совпадать с длиной текста.print(DATALENGTH(' ')) --1 print(LEN(' ')) --0
Когда дело доходит до равенства выражений, две строки сравниваются на равенство следующим образом:
Это средний шаг, который приводит к неожиданным результатам - после этого шага вы эффективно сравниваете пробелы с пробелами - следовательно, они считаются равными.
LIKE
ведет себя лучше, чем=
в ситуации с "пробелами", потому что не выполняет заполнение пробелами по шаблону, который вы пытались сопоставить:Дам
eq
пока:Даст
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
источник
sql-server-2008 r2
я получил@Space Not Like @Space2 @Space2 Not Like @Space
. Есть идеи, почему?@Space Not Like @Space2 @Space2 Not Like @Space
Оператор = означает, что T-SQL не столько «равно», сколько «это одно и то же слово / фраза в соответствии с сопоставлением контекста выражения», а LEN - это «количество символов в слове / фразе». Никакие параметры сортировки не рассматривают завершающие пробелы как часть предшествующего им слова / фразы (хотя они рассматривают начальные пробелы как часть предшествующей им строки).
Если вам нужно отличить «это» от «это», не следует использовать оператор «это одно и то же слово или фраза», потому что «это» и «это» - одно и то же слово.
Способствовать = works является идея о том, что оператор равенства строк должен зависеть от содержимого своих аргументов и от контекста сопоставления выражения, но он не должен зависеть от типов аргументов, если они оба являются строковыми типами. .
Концепция естественного языка «это одно и то же слово» обычно недостаточно точна, чтобы ее можно было уловить математическим оператором, таким как =, и в естественном языке нет концепции строкового типа. Контекст (то есть сопоставление) имеет значение (и существует на естественном языке) и является частью истории, а дополнительные свойства (некоторые, которые кажутся причудливыми) являются частью определения =, чтобы сделать его четко определенным в неестественном мире данные.
Что касается типа, вы не хотите, чтобы слова менялись, когда они хранятся в строках разных типов. Например, типы VARCHAR (10), CHAR (10) и CHAR (3) могут содержать представления слова «кошка» и? = 'cat' должно позволить нам решить, содержит ли значение любого из этих типов слово 'cat' (с проблемами регистра и акцента, определяемыми сопоставлением).
Ответ на комментарий JohnFx:
См. Раздел Использование данных char и varchar в электронной документации. Цитата с этой страницы, акцент мой:
Я согласен, что это было бы проще найти, но это задокументировано.
Стоит также отметить, что семантика SQL, где = имеет отношение к реальным данным и контексту сравнения (в отличие от чего-то, что связано с битами, хранящимися на компьютере), долгое время была частью SQL. Предпосылкой для СУБД и SQL является точное представление реальных данных, поэтому они поддерживают сопоставления за много лет до того, как аналогичные идеи (такие как CultureInfo) вошли в сферу алголо-подобных языков. Предпосылкой этих языков (по крайней мере, до недавнего времени) было решение инженерных проблем, а не управление бизнес-данными. (В последнее время использование подобных языков в не инженерных приложениях, таких как поиск, набирает обороты, но Java, C # и т. Д. Все еще борются со своими некоммерческими корнями.)
На мой взгляд, несправедливо критиковать SQL за то, что он отличается от «большинства языков программирования». SQL был разработан для поддержки структуры моделирования бизнес-данных, которая сильно отличается от инженерной, поэтому язык другой (и лучше для его цели).
Черт возьми, когда SQL был впервые указан, некоторые языки не имели встроенного строкового типа. И в некоторых языках по-прежнему оператор равенства между строками вообще не сравнивает символьные данные, а сравнивает ссылки! Меня не удивит, если через десять-два десятилетия идея, что == зависит от культуры, станет нормой.
источник
Я нашел эту статью в блоге, в которой описывается поведение и объясняется, почему.
Дополнительная информация также доступна в MSKB316626
источник
Некоторое время назад был похожий вопрос, где я рассмотрел аналогичную проблему здесь
Вместо
LEN(' ')
использованияDATALENGTH(' ')
- это дает вам правильное значение.Решения заключались в том, чтобы использовать
LIKE
предложение, как объяснено в моем ответе, и / или включить второе условие вWHERE
предложение для проверкиDATALENGTH
.Прочтите этот вопрос и ссылки там.
источник
Чтобы сравнить значение с буквальным пространством, вы также можете использовать этот метод в качестве альтернативы оператору LIKE:
источник
Как различать записи при выборе с полями 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 есть индекс, он будет использоваться в каждом случае.
Надеюсь, это будет полезно.
источник
Другой способ - вернуть его в состояние, в котором пространство имеет значение. например: замените пробел символом, известным как _
if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_') print 'equal' else print 'not equal'
доходность: не равно
Не идеально и, вероятно, медленно, но это еще один быстрый путь вперед, когда нужно быстро.
источник
Иногда приходится иметь дело с пробелами в данных, с любыми другими символами или без них, даже если идея использования Null лучше, но не всегда применима. Я столкнулся с описанной ситуацией и решил ее следующим образом:
Конечно, вы бы не сделали этого для большого количества данных, но это работает быстро и легко для нескольких сотен строк ...
источник