SQL Server 2008空字符串与空间


82

今天早上我碰到了一些奇怪的事情,以为我要把它提交评论。

有人可以解释为什么针对SQL 2008运行以下SQL查询时会显示“等于”。数据库兼容性级别设置为100。

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

这将返回0:

select (LEN(' '))

它似乎正在自动修剪空间。我不知道在以前的SQL Server版本中是否是这种情况,而且我什至没有办法进行测试。

我遇到这个问题是因为生产查询返回的结果不正确。我找不到任何地方记录此行为。

有人对此有任何信息吗?


2
SQL 2005:select len('')返回0
Mayo

1
它在SQL Server上相同的2000年
皮埃尔-阿兰·Vigeant

1
这是一个有趣的问题。无论您在两个字符串中放置多少个空格,无论它们是否匹配,它似乎都返回相等。经过更多的实验后,我注意到在比较之前,它实际上在相等运算符的两侧进行了RTRIM。看起来您已经获得LEN函数的答案,但是对于您的问题的相等性部分,我真正感兴趣的是一个比“ TSQ中的varchars和相等性棘手”更彻底的答案。
JohnFx

我相信Oracle也这样做。
quillbreaker

通常,我发现存储空字符串是一个坏主意,这是原因之一。我更喜欢使用Null并在人们尝试将null信息转换为诸如空字符串或超出正常范围的数据方式之类的值时发现许多问题。
HLGEM 2011年

Answers:


87

varcharS和相等性在TSQL中很棘手。该LEN函数表示:

返回给定字符串表达式的字符数,而不是字节数,不包括结尾的blanks

您需要使用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

1
好答案。我在LEN文档中没有注意到这一点。不仅限于LEN。RIGHT和LEFT函数表现出相似的行为,但是没有记录。这似乎是带有引起问题的空格的文字。我注意到这也返回等于:如果''= SPACE(1)print'equal'else print'not equal'我对获取真实长度并不真正感兴趣,我只是感到困惑,为什么当我在其中寻找空格时列,则返回所有为空字符串的列。
jhale

另外,有关LIKE语句的详细信息。我想这个故事的寓意是尽量不要让自己处于需要比较空格和空字符串的位置。
jhale

2
问题大于将空格与空字符串进行比较。比较以不同数量的空格结尾的任意两个字符串会表现出相同的行为。
JohnFx

3
@butterchicken:对这么晚的帖子很抱歉,我只是看到了这个问题,但是当我在我的这个(最后一个)上运行时,我sql-server-2008 r2得到了@Space Not Like @Space2 @Space2 Not Like @Space 。知道为什么吗?
Razort4x

1
在SQL Server 2012和SQL Server 2014上确认,结果是@Space Not Like @Space2 @Space2 Not Like @Space
只是学习者

19

=运算符不是T-SQL,而是“等于”,而是“根据表达式上下文的校对是相同的单词/短语”,而LEN是“单词/短语中的字符数”。没有排序规则将尾随空格视为它们前面的单词/短语的一部分(尽管它们确实将前导空格视为它们前面的字符串的一部分)。

如果您需要将“ this”与“ this”区分开来,则不应使用“相同的单词或短语”运算符,因为“ this”和“ this”是相同的单词。

促成=工作方式的想法是,字符串相等运算符应取决于其参数的内容和表达式的排序规则上下文,但不应取决于参数的类型(如果它们都是字符串类型) 。

“这些是同一单词”的自然语言概念通常不够精确,无法被=等数学运算符捕获,自然语言中没有字符串类型的概念。上下文(即排序规则)很重要(并且以自然语言存在),并且是故事的一部分,而其他属性(某些看起来很古怪的属性)则是=定义的一部分,以使其在非自然世界中得到良好的定义。数据。

在类型问题上,当单词以不同的字符串类型存储时,您不希望更改它们。例如,类型VARCHAR(10),CHAR(10)和CHAR(3)都可以容纳单词'cat'的表示形式,而?='cat'应该让我们决定这些类型中的任何一个值是否包含单词'cat'(大小写和重音由排序规则确定)。

回应JohnFx的评论:

请参阅联机丛书中的使用char和varchar数据。从该页面引用,重点是:

每个char和varchar数据值都有一个排序规则。归类定义属性,例如用于表示每个字符的位模式,比较规则以及对大小写或重音符号的 敏感性。

我同意可能更容易找到,但已记录在案。

同样值得一提的是,SQL的语义(其中=与实际数据有关)和比较的上下文(与存储在计算机上的某些位相对)早已是SQL的一部分。RDBMS和SQL的前提是真实数据的忠实表示,因此,在类似概念(例如CultureInfo)进入类似Algol的语言领域之前,它就支持排序规则。这些语言(至少直到最近)的前提是解决工程问题,而不是管理业务数据。(最近,在非工程应用程序(例如搜索)中使用类似的语言已经取得了一些进展,但是Java,C#等仍在其非商业根源方面苦苦挣扎。)

在我看来,批评SQL与“大多数编程语言”不同是不公平的。SQL被设计为支持业务数据建模的框架,该框架与工程设计有很大的不同,因此语言是不同的(并且对于目标而言更好)。

哎呀,当第一次指定SQL时,某些语言没有任何内置的字符串类型。而且在某些语言中,字符串之间的equals运算符根本不比较字符数据,而是比较引用!如果再过一两年,==取决于文化的想法成为常态,这也不会令我感到惊讶。


BOL这样描述了=运算符:“比较两个表达式的相等性(比较运算符)”。无论该行为是否正确,您都必须承认,就大多数编程语言而言,此操作符的用法非常混乱且不规范。MS至少应在文档中添加有关此行为的警告。
JohnFx

@JohnFx:在我的回答中看到我的评论太长了。
史蒂夫·卡斯

9

我发现这篇博客文章描述了行为并解释了原因。

SQL标准要求字符串比较有效地用空格字符填充较短的字符串。 这导致了令人惊讶的结果,即N''= N''(空字符串等于一个或多个空格字符的字符串),并且更普遍的是,如果它们之间仅尾随空格不同,则任何字符串都等于另一个字符串。在某些情况下这可能是个问题。

MSKB316626中也提供了更多信息


谢谢。我很惊讶它符合标准。我敢肯定有人比我聪明得多,这是有充分理由的。
jhale

@John:您的意思是在评论中写≠(不等于)吗?
史蒂夫·卡斯

原始报价中有一个错误,我直接复制了。我更新了报价以反映原始作者的意思。
JohnFx

5

前段时间有一个类似的问题,我在这里调查了一个类似的问题

代替LEN(' '),使用DATALENGTH(' ')-为您提供正确的值。

解决方案是使用LIKE我的答案中所解释的子句,和/或在WHERE子句中DATALENGTH也包括第二条件进行检查。

阅读该问题及其中的链接。


3

要将值与文字空间进行比较,您还可以使用此技术来代替LIKE语句:

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

0

如何在SQL Server上使用char / varchar字段区分select上的记录:示例:

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上有一个索引,则将在每种情况下使用它。

希望对您有所帮助。


0

另一种方法是将其放回该空间具有价值的状态。例如:用已知的字符_代替空格

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

返回:不等于

这并不理想,而且可能很慢,但是在需要快速处理时,这是另一种快速的解决方法。


0

有时,即使使用Null的想法更好,但有时还是必须处理数据中的空格(带或不带其他任何字符),但并非总是可用。我确实遇到了上述情况,并通过以下方式解决了问题:

... where ('>' + @space + '<') <> ('>' + @space2 + '<')

当然,您不会对大量数据执行此操作,但是对于数百行而言,它可以快速简便地工作...


1
问题是为什么SQL Server会表现得如此,而不是通常如何处理这种行为。jhale可能不会修改他的程序代码,而只会修改他的服务器配置。
Lutz Prechelt 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.