Sql server 为什么搜索n个空格字符的字符串会返回不一致的结果?

Sql server 为什么搜索n个空格字符的字符串会返回不一致的结果?,sql-server,tsql,Sql Server,Tsql,客户发现了一个bug,在试图修复它时,我遇到了这种奇怪的行为。我已经花了几个小时针对这个问题运行了不同的测试,我仍然在努力理解它。它绝对可以在SQL2016和2017(最新CU)以及不同版本的management studio上复制。下面的示例是一个取自1000行程序的简化示例: 要开始创建必要的对象,请执行以下操作: CREATE DATABASE TestDB; GO USE TestDB; CREATE TABLE test ( id int IDENTITY, pdate datetim

客户发现了一个bug,在试图修复它时,我遇到了这种奇怪的行为。我已经花了几个小时针对这个问题运行了不同的测试,我仍然在努力理解它。它绝对可以在SQL2016和2017(最新CU)以及不同版本的management studio上复制。下面的示例是一个取自1000行程序的简化示例:

要开始创建必要的对象,请执行以下操作:

CREATE DATABASE TestDB;
GO
USE TestDB;
CREATE TABLE test (
id int IDENTITY,
pdate datetime);
INSERT test
VALUES('1-Feb-2018'),('1-Mar-2018'),(NULL);
现在快速查看以验证表中的值:

SELECT * FROM test
现在我想将pdate转换为特定格式,如果pdate为NULL,则返回一个空字符串:

SELECT * FROM
    (
        SELECT
            id,
            IIF(pdate IS NULL, '', FORMAT(pdate, 'yyyy-MM-dd')) pdate2
        FROM test
    ) a
WHERE pdate2 = ''
上面的查询生成id 3,其中包含一个空字符串,这是预期的结果。但当我在引号之间放置任意数量的空格字符时,结果中仍然会出现相同的行:

SELECT * FROM
    (
        SELECT
            id,
            IIF(pdate IS NULL, '', FORMAT(pdate, 'yyyy-MM-dd')) pdate2
        FROM test
    ) a
WHERE pdate2 = '    ' 
为什么会发生这种情况

如果我使用LIKE代替equals,而不使用通配符,即:

SELECT * FROM
    (
        SELECT
            id,
            IIF(pdate IS NULL, '', FORMAT(pdate, 'yyyy-MM-dd')) pdate2
        FROM test
    ) a
WHERE pdate2 LIKE '    '
这给了我预期的结果-没有返回行


这是一个bug,还是一个空字符串被认为等于一个由n个空格字符组成的字符串的深层次技术原因?如果是这样,那么为什么LIKE会给出正确的结果呢?

根据ANSI SQL标准,字符字段比较通常会忽略尾随空格。因此:

SELECT 1 WHERE '' = '     '
将返回
1

描述此行为(我的重点):

SQL Server遵循ANSI/ISO SQL-92规范(第8.2节, ,关于如何比较字符串的一般规则#3) 有空格ANSI标准要求字符填充 用于比较的字符串,以便它们的长度在比较之前匹配 比较它们。填充直接影响WHERE的语义 并具有子句谓词和其他Transact-SQL字符串 比较。例如,Transact-SQL考虑字符串“abc”和 对于大多数比较操作,“abc”是等效的

此规则的唯一例外是LIKE谓词。什么时候合适 LIKE谓词表达式的侧面具有一个带有尾随符号的值 空间,SQL Server不会将这两个值填充到相同的长度 在比较发生之前。因为类似的目的 根据定义,谓词是为了方便模式搜索,而不是 与简单的字符串相等性测试相比,这并不违反 符合前面提到的ANSI SQL-92规范

[……]

SET ANSI_PADDING设置不影响SQL Server是否填充 字符串,然后再进行比较。设置ANSI_填充仅影响是否 从插入到表中的值中修剪尾随空格, 因此,它影响存储,但不影响比较

请注意,它们也涵盖了您发现的与LIKE的差异


Brent Ozar在博客中介绍了这种行为。

旁白:From:“返回指定字符串表达式的字符数,不包括尾随空格。”不排除尾随空格。对于Unicode字符串,您可以使用
DataLength(Unicode DestringExpression)/DataLength(N'#')
获取字符长度。一般来说,
DataLength(左(Coalesce(StringExpression,#),1))
将返回每个字符的字节数。这很酷,我希望我昨天在测试时就知道这一点!谢谢这完美地回答了我的问题。我仍然觉得空字符串可以被认为与只包含空格字符的字符串“相等”,这有点令人困惑。很高兴有一个解释,虽然,我没有失去我的大理石。谢谢。@MrWest请记住,最初,CHAR是SQL中字符串数据的唯一数据类型,这可能会有所帮助。也就是说,最初所有字符数据总是填充到字段的整个长度。后来添加了VARCHAR。