SQL Server 2008空string与空间
我今天早上遇到了一些奇怪的事情,并认为我会提交评论。
有人可以解释为什么下面的SQL查询打印“平等”对SQL 2008运行时。数据库兼容性级别设置为100。
if '' = ' ' print 'equal' else print 'not equal'
这返回0:
select (LEN(' '))
它似乎是自动修剪的空间。 我不知道在以前的SQL Server版本中是否出现这种情况,而且我不再有任何testing。
我碰到这个,因为生产查询返回不正确的结果。 我无法在任何地方find这种行为。
有没有人有这方面的任何信息?
TSQL中的varchar
和equal是棘手的。 LEN
函数说:
返回给定stringexpression式的字符数(而不是字节数), 不包括尾随空白 。
您需要使用DATALENGTH
来获取有关数据的真实byte
数。 如果你有unicode数据,请注意在这种情况下你得到的值不会和文本的长度一样。
print(DATALENGTH(' ')) --1 print(LEN(' ')) --0
当谈到expression的平等时,这两个string比较是这样的平等:
- 获取更短的string
- 填充空白直到长度等于较长的string
- 比较两者
这是导致意想不到的结果的中间步骤 – 在这一步之后,您有效地比较了空白和空白 – 因此它们是平等的。
LIKE
在“空白”情况下performance得比=
,因为它不会在你试图匹配的模式上执行空白填充:
if '' = ' ' print 'eq' else print 'ne'
给以下时间:
if '' LIKE ' ' print 'eq' else print 'ne'
会给ne
小心LIKE
虽然:它不是对称的:它将尾部空格视为模式(RHS)中的重要部分,而不是匹配expression式(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并不是“等于”,而是“根据expression式上下文的归类是相同的词/短语”,而LEN是“词/短语中的字符数”。 没有归类将尾随空白作为前面的单词/短语的一部分(尽pipe它们将前导空白视为它们之前的string的一部分)。
如果您需要将“this”与“this”区分开来,则不应该使用“是相同的单词或短语”运算符,因为“this”和“this”是相同的单词。
对way = works的贡献是这样的思想,即string相等运算符应该依赖于它的参数的内容和expression式的sorting上下文,但是它不应该依赖于参数的types,如果它们都是stringtypes。
“这些都是同一个词”的自然语言概念通常不够精确,不能被像=这样的math运算符捕获,而且在自然语言中没有stringtypes的概念。 上下文(即整理)是重要的(并且以自然语言存在)并且是故事的一部分,并且附加属性(有些似乎古怪的)是=的定义的一部分,以便在非自然世界中数据。
在types问题上,当用不同的stringtypes存储时,你不希望单词改变。 例如,typesVARCHAR(10),CHAR(10)和CHAR(3)都可以表示单词“cat”,并且? ='猫'应让我们决定这些types的任何一个值是否包含“猫”一词(由归类确定的大小写和重音问题)。
回应JohnFx的评论:
请参阅在联机丛书中使用char和varchar数据 。 引用该页面,重点是我的:
每个char和varchar数据值都有一个sorting规则。 sorting规则定义了一些属性,如用于表示每个字符的位模式, 比较规则以及对大小写或重音的敏感度。
我同意这可能更容易find,但它被logging。
值得注意的是,SQL的语义,其中=与现实世界的数据和比较的上下文(而不是关于存储在计算机上的比特的东西)有很长一段时间一直是SQL的一部分。 RDBMSs和SQL的前提是真实世界数据的忠实表示,因此它在类似的想法(比如CultureInfo)进入Algol-like语言领域之前多年的支持sorting。 这些语言(至less直到最近)的前提是工程问题的解决,而不是商业数据的pipe理。 (最近,在search等非工程应用中使用类似的语言正在取得一些进展,但Java,C#等仍然在与他们的非商业根源挣扎。)
在我看来,批评SQL与“大多数编程语言”不同是不公平的。 SQL旨在支持与工程学非常不同的业务数据build模框架,因此语言是不同的(对于其目标更好)。
嘿,当第一次指定SQL时,一些语言没有任何内置的stringtypes。 而在某些语言中,string之间的等号运算符根本不比较字符数据,而是比较引用! 如果在另外的十年或二十年中,==依赖于文化的观念成为常态,这并不会让我感到惊讶。
我发现这篇博客文章描述了行为并解释了原因。
SQL标准要求string比较有效地使用空格字符填充较短的string。 这导致令人惊讶的结果N“= N”(空string等于一个或多个空格字符的string),更一般地,任何string等于另一个string,如果它们只有尾随空格不同。 这在某些情况下可能是个问题。
更多信息也可在MSKB316626中find
前段时间有一个类似的问题,我在这里看到类似的问题
而不是LEN(''),使用DATALENGTH('') – 给你正确的值。
解决scheme是使用LIKE子句,如我在那里的答案中所解释的那样,和/或在WHERE子句中包含第二个条件来检查DATALENGTH。
在那里阅读这个问题和链接。
要将一个值与一个文字空间进行比较,您也可以使用这种技术来替代LIKE语句:
IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'
有时候不得不处理数据中的空格,不pipe有没有其他字符,即使使用Null的想法更好 – 但并不总是可用的。 我遇到了所描述的情况,并通过这种方式解决了这个问题:
… where('>'+ @space +'<')<>('>'+ @ space2 +'<')
当然,你不会做大量的数据,但它可以快速,方便地运行一百行。
赫伯特
如何在sql server上使用字段char / varcharselect不同的logging:例如:
declare @mayvar as varchar(10) set @mayvar = 'data ' select mykey, myfield from mytable where myfield = @mayvar
预期
mykey(int)| myfield(varchar10)
1 | 'data'
获得
mykey | MyField的
1 | 'data'2 | 'data'
即使我编写select mykey, myfield from mytable where myfield = 'data'
(没有最后的空白),我得到相同的结果。
我如何解决? 在这种模式下:
select mykey, myfield from mytable where myfield = @mayvar and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)
如果在myfield上有一个索引,它将在每种情况下使用。
我希望这会有帮助。