【问题标题】:SQL WHERE clause matching values with trailing spacesSQL WHERE 子句将值与尾随空格匹配
【发布时间】:2011-05-09 03:30:19
【问题描述】:

在 SQL Server 2008 中,我有一个名为 Zone 的表,其中有一个列 ZoneReference varchar(50) not null 作为主键。

如果我运行以下查询:

select '"' + ZoneReference + '"' as QuotedZoneReference
from Zone
where ZoneReference = 'WF11XU'

我得到以下结果:

"WF11XU "

注意尾随空格。

这怎么可能?如果该行上确实存在尾随空格,那么我希望返回 zero 结果,所以我假设它是 SQL Server Management Studio 奇怪地显示的其他内容。

在 C# 代码中调用 zoneReference.Trim() 会删除它,这表明它是某种空白字符。

谁能帮忙?

【问题讨论】:

    标签: c# sql sql-server database sql-server-2008


    【解决方案1】:

    这是预期的结果:在 SQL Server 中,= 运算符在进行比较时会忽略尾随空格。

    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 规范部分。

    Source

    【讨论】:

    • 我相信WHERE ZoneReference = 'WF11XU' AND DATALENGTH(ZoneReference) = DATALENGTH('WF11XU') 也会起作用,并且可能比LIKE 更快
    • @MarkByers 是的,我明白了 - 我试过 where 'WF11XU' like ZoneReference 并且成功了!越来越诡异。不过,每天都是上学日!
    • LIKE 不能双向工作,SELECT IIF('WF11XU ' LIKE 'WF11XU', 1, 0) 返回1
    【解决方案2】:

    尾随空格并不总是被忽略。 我今天遇到了这个问题。我的表有 NCHAR 列并且正在连接到 VARCHAR 数据。 由于表中的数据没有字段宽,SQL Server 会自动添加尾随空格。

    我有一个采用 varchar 参数的 ITVF(内联表值函数)。 这些参数用于与具有 NCHAR 字段的表的 JOIN。

    连接失败,因为传递给函数的数据没有尾随空格,但表中的数据有。为什么会这样?

    我被数据类型优先级绊倒了。 (见http://technet.microsoft.com/en-us/library/ms190309.aspx

    在比较不同类型的字符串时,先将低优先级类型转换为高优先级类型再进行比较。所以我的 VARCHAR 参数被转换为 NCHAR。比较了 NCHAR,显然空间​​很重要。

    我是如何解决这个问题的?我更改了函数定义以使用 NVARCHAR 参数,这些参数的优先级高于 NCHAR。现在 SQL Server 自动将 NCHAR 更改为 NVARCHAR,并且忽略尾随空格。

    我为什么不直接执行 RTRIM?测试表明,RTRIM 降低了性能,阻止了 SQL Server 原本会使用的 JOIN 优化。

    为什么不改变表的数据类型呢?这些表已经安装在客户站点上,他们不想运行维护脚本(时间+金钱来支付 DBA)或让我们访问他们的机器(可以理解)。

    【讨论】:

      【解决方案3】:

      是的,马克是正确的。运行以下 SQL:

      create table #temp (name varchar(15))
      insert into #temp values ('james ')
      select '"' + name + '"' from #temp where name ='james'
      select '"' + name + '"' from #temp where name like 'james'
      drop table #temp
      

      但是,关于“like”语句的断言在上面的示例中似乎不起作用。输出:

      (1 row(s) affected)
      
      -----------------
      "james "
      
      (1 row(s) affected)
      
      
      -----------------
      "james "
      
      (1 row(s) affected)
      

      编辑: 为了让它工作,你可以把它放在最后:

      and name <> rtrim(ltrim(name))
      

      虽然丑。

      EDIT2: 鉴于上面的 cmets,以下将起作用:

      select '"' + name + '"' from #temp where 'james' like name
      

      【讨论】:

        【解决方案4】:

        试试

            select Replace('"' + ZoneReference + '"'," ", "") as QuotedZoneReference from Zone where ZoneReference = 'WF11XU'
        

        【讨论】:

        • 你有点没抓住重点。关键是我没想到where 子句会匹配这条记录。
        • 是的,我认为 Neil 知道该怎么做,他只是想知道为什么他会看到观察到的行为。
        • 恐怕我投了反对票,因为这篇文章没有帮助,因为它偏离了正在回答的实际问题。
        猜你喜欢
        • 2019-07-14
        • 2020-04-24
        • 1970-01-01
        • 2021-05-18
        • 2017-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多