SQL Server 2008 空字符串与空格

Posted

技术标签:

【中文标题】SQL Server 2008 空字符串与空格【英文标题】:SQL Server 2008 Empty String vs. Space 【发布时间】:2010-11-26 20:33:32 【问题描述】:

今天早上我遇到了一些奇怪的事情,并认为我应该提交它以供评论。

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

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

这会返回 0:

select (LEN(' '))

它似乎在自动修剪空间。我不知道在以前版本的 SQL Server 中是否是这种情况,而且我已经没有任何地方可以测试它了。

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

有人知道这方面的信息吗?

【问题讨论】:

SQL 2005:选择 len(' ') 返回 0 在 Sql Server 2000 上也是如此。 这是一个有趣的问题。无论您在任一字符串中放置多少空格,无论它们是否匹配,它似乎都返回相等。经过更多的实验后,我注意到它在比较之前有效地在相等运算符的两侧进行了 RTRIM。看起来您在 LEN 函数上得到了答案,但对于您的问题的相等部分,我真的很想得到一个比“varchars 和相等性在 TSQ 中很棘手”更彻底的答案。 Oracle 也这样做,我相信。 总的来说,我发现存储空字符串是个坏主意,这也是原因之一。我更喜欢使用 Null 并且当人们试图将 null 信息变成像空字符串这样的值或超出正常范围的数据时会发现很多问题。 【参考方案1】:

varchars 和相等在 TSQL 中很棘手。 LEN 函数说:

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

您需要使用DATALENGTH 来获得真正的byte 相关数据计数。如果你有 unicode 数据,请注意在这种情况下你得到的值不会和文本的长度一样。

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

当涉及到表达式的相等性时,两个字符串的相等性比较如下:

获取更短的字符串 用空格填充直到长度等于更长的字符串 比较两者

这是导致意外结果的中间步骤 - 在该步骤之后,您可以有效地将空白与空白进行比较 - 因此它们被视为相等。

LIKE 在“空白”情况下的表现优于=,因为它不会对您尝试匹配的模式执行空白填充:

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

将给eq while:

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

【讨论】:

不错的答案。我没有在 LEN 文档中注意到这一点。不过,它不仅限于 LEN。 RIGHT 和 LEFT 函数表现出类似的行为,但没有记录。似乎是带有空格的文字导致了问题。我注意到这也返回相等: if '' = SPACE(1) print 'equal' else print 'not equal' 我对获得真正的长度并不感兴趣,我只是很困惑为什么当我在寻找空间时一列,所有为空字符串的列都被返回。 另外,关于 LIKE 语句的好信息。我想这个故事的寓意是尽量不要让自己处于需要比较空格和空字符串的位置。 问题比将空格与空字符串进行比较更大。比较任何两个以不同数量的空格结尾的字符串会表现出相同的行为。 @butterchicken:抱歉这么晚了,我刚看到这个问题,但是当我在sql-server-2008 r2 上运行这个(最后一个)时,我得到了@Space Not Like @Space2 @Space2 Not Like @Space 。知道为什么吗? 在 SQL Server 2012 & SQL Server 2014 上确认,结果为@Space Not Like @Space2 @Space2 Not Like @Space 【参考方案2】:

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

如果您需要区分“this”和“this”,则不应使用“同一个词或短语”运算符,因为“this”和“this”是同一个词。

有助于 = 工作方式的想法是字符串相等运算符应依赖于其参数的内容和表达式的排序规则上下文,但它不应该依赖于参数的类型,如果它们是两种字符串类型。

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

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

对 JohnFx 评论的回应:

请参阅联机丛书中的Using char and varchar Data。引用该页面,强调我的:

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

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

同样值得注意的是,SQL 的语义,其中 = 与现实世界的数据和比较的上下文有关(与存储在计算机上的位不同)长期以来一直是 SQL 的一部分时间。 RDBMS 和 SQL 的前提是真实世界数据的忠实表示,因此在类似想法(例如 CultureInfo)进入类 Algol 语言领域之前很多年它就支持排序规则。这些语言的前提(至少直到最近)是解决工程中的问题,而不是管理业务数据。 (最近,在搜索等非工程应用程序中使用类似语言正在取得一些进展,但 Java、C# 等仍在与它们的非商业根源作斗争。)

在我看来,批评 SQL 与“大多数编程语言”不同是不公平的。 SQL 旨在支持与工程非常不同的业务数据建模框架,因此语言不同(并且更适合其目标)。

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

【讨论】:

BOL 如此描述 = 运算符:“比较两个表达式的相等性(比较运算符)。”无论行为是否正确,您都必须承认,就大多数编程语言中该运算符的使用而言,它非常令人困惑且不标准。 MS 至少应该在文档中添加有关此行为的警告。 @JohnFx:在我的回答中查看我太长的评论回复。【参考方案3】:

我发现了这个blog article,它描述了这种行为并解释了原因。

SQL 标准要求该字符串 比较,有效地,垫 带有空格字符的较短字符串。 这导致了令人惊讶的结果 那 N'' = N' ' (空字符串 等于一个或多个空格的字符串 字符)和更普遍的任何 字符串等于另一个字符串,如果他们 仅通过尾随空格不同。这 在某些情况下可能是个问题。

更多信息也可通过 MSKB316626

获得

【讨论】:

谢谢。我很惊讶它在标准中。我敢肯定,比我聪明得多的人有充分的理由这样做。 @John:你的意思是在你的评论中写上≠(不等于)吗? 原始报价有错误,我直接复制了。我更新了引用以反映原作者的意思。【参考方案4】:

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

使用DATALENGTH(' ') 而不是LEN(' ') - 这会为您提供正确的值。

解决方案是使用LIKE 子句,如我在其中的回答中所述,和/或在WHERE 子句中包含第二个条件以检查DATALENGTH

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

如何在 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 | '数据'

获得

我的钥匙 |我的领域

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

希望对你有所帮助。

【讨论】:

【参考方案7】:

另一种方法是让它回到空间有价值的状态。 例如:用 _ 之类的字符替换空格

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

返回:不等于

不理想,可能很慢,但在需要时是另一种快速前进的方式。

【讨论】:

【参考方案8】:

有时必须处理数据中的空格,无论是否有任何其他字符,即使使用 Null 的想法更好 - 但并不总是可用。 我确实遇到了所描述的情况并以这种方式解决了它:

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

当然,您不会对大量数据执行此操作,但它可以快速轻松地处理数百行...

【讨论】:

问题是为什么 SQL 服务器的行为如此,而不是一般如何处理这种行为。 jhale 可能宁愿不修改他的程序代码,只修改他的服务器配置。

以上是关于SQL Server 2008 空字符串与空格的主要内容,如果未能解决你的问题,请参考以下文章

将空字符串插入 SQL Server 的 INT 列

hive的空字符串与null

href=""中的空字符串与'#' [重复]

Python:将空字符串与 False 进行比较是 False,为啥?

Python:将空字符串与False进行比较是假的,为什么?

Java空字符串与null的区别和判断字符串是不是为空的方法