84

今天早上我遇到了一些奇怪的事情,我想我会把它提交给评论。

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

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

这将返回 0:

select (LEN(' '))

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

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

有人有这方面的信息吗?

4

8 回答 8

92

varchars 和相等在 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
于 2009-09-09T14:14:21.460 回答
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 时,一些语言没有任何内置的字符串类型。而且在某些语言中,字符串之间的等号运算符根本不比较字符数据,而是比较引用!如果再过一两年,== 依赖于文化的想法成为常态,我不会感到惊讶。

于 2009-09-09T15:20:55.823 回答
9

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

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

MSKB316626中还提供了更多信息

于 2009-09-09T15:03:21.603 回答
5

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

而不是LEN(' '),使用DATALENGTH(' ')- 给你正确的价值。

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

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

于 2009-09-09T14:12:25.623 回答
3

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

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'
于 2011-02-24T20:03:11.973 回答
0

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

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

当然,对于大量数据,您不会这样做,但它可以快速轻松地处理数百行......

于 2015-01-16T14:37:51.630 回答
0

如何在 sql server 上使用字段 char/varchar 区分 select 记录:示例:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

预期的

mykey (int) | 我的字段(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 上有索引,则在每种情况下都会使用它。

我希望它会有所帮助。

于 2015-04-14T15:45:59.903 回答
0

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

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

返回:不等于

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

于 2019-04-12T03:09:19.627 回答