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

时间:2018-08-03 22:07:52

标签: sql-server tsql

客户发现了一个错误,在尝试对其进行修复时,我遇到了这种奇怪的现象。我已经花了几个小时对它进行各种测试,但我仍在努力理解它。绝对可以在SQL2016和2017(最新CU)和不同版本的Management Studio上重现。下面的示例是从1000行proc中获取的简化示例:

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

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,即:

SELECT * FROM
    (
        SELECT
            id,
            IIF(pdate IS NULL, '', FORMAT(pdate, 'yyyy-MM-dd')) pdate2
        FROM test
    ) a
WHERE pdate2 LIKE '    '

这给了我预期的结果-没有返回行。

这是一个错误,还是有一些深层的技术原因可将空字符串视为等于n个空格字符的字符串?如果是这样,那么为什么LIKE给我正确的结果?

1 个答案:

答案 0 :(得分:2)

根据ANSI SQL标准,字符字段比较通常忽略尾随空格。所以这个:

SELECT 1 WHERE '' = '     '

将返回1

KB316626 INF: How SQL Server Compares Strings with Trailing Spaces 描述了这种行为(强调我的行为):

  

SQL Server遵循ANSI / ISO SQL-92规范(第8.2节,   ,关于如何比较字符串的通用规则3)   与空格。 ANSI标准要求字符填充   比较中使用的字符串,以便它们的长度匹配之前   比较它们。填充直接影响WHERE的语义   和HAVING子句谓词以及其他Transact-SQL字符串   比较。例如,Transact-SQL将字符串“ abc”和   'abc'与大多数比较操作等效。

     

此规则的唯一例外是LIKE谓词。何时正确   LIKE谓词表达式的一侧具有带尾随的值   空间,SQL Server不会将两个值填充到相同的长度   在进行比较之前。因为LIKE的目的   根据定义,谓词是为了促进模式搜索   比简单的字符串相等测试,这不违反本节   前面提到的ANSI SQL-92规范。

     

[...]

     

SET ANSI_PADDING设置不会影响SQL Server填充   字符串比较之前。 SET ANSI_PADDING仅影响是否   从插入表中的值中修剪掉尾随的空白,   因此它会影响存储,但不会影响比较。

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

Brent Ozar关于此行为here的博客。