问题 如何使SQL Server返回FALSE以比较带有和不带尾随空格的varchars?


如果我故意将尾随空格存储在一个 VARCHAR 专栏,如何强制SQL Server将数据视为不匹配?

SELECT 'foo' WHERE 'bar' = 'bar    '

我努力了:

SELECT 'foo' WHERE LEN('bar') = LEN('bar    ')

我看到浮动的一种方法是在每个字符串的末尾添加一个特定的字符,然后将其删除以供我的演示文稿...但这看起来很傻。

有没有一种方法我忽略了?

我注意到它不适用于 领导 所以也许我运行一个函数,在比较之前反转字符顺序....问题是这使得查询unSARGable ....


1930
2017-10-15 00:21


起源



答案:


来自文档 LEN(Transact-SQL)

返回指定字符串表达式的字符数, 不包括尾随空白。要返回用于表示表达式的字节数,请使用 DATALENGTH 功能

另外,从支持页面上 SQL Server如何将字符串与尾随空格进行比较

SQL Server遵循ANSI / ISO SQL-92规范,介绍如何将字符串与空格进行比较。 ANSI标准要求 填充比较中使用的字符串,以便在比较它们之前匹配它们的长度

更新我删除了我的代码 LIKE (在比较期间不填充空格)和 DATALENGTH() 因为它们对比较字符串并非万无一失

在许多其他地方也有人问过其他解决方案:


6
2017-10-15 04:06



我认为 DATALENGTH 我会在这里很好地工作,或存储计算 DATALENGTH 在一列中,以一个字节为代价使其更快。 - Matthew
这并没有回答所写的问题。它错误地声称这是关于这个问题的行为问题的重复 明确地 要求避免。 - Kenny Evitt


答案:


来自文档 LEN(Transact-SQL)

返回指定字符串表达式的字符数, 不包括尾随空白。要返回用于表示表达式的字节数,请使用 DATALENGTH 功能

另外,从支持页面上 SQL Server如何将字符串与尾随空格进行比较

SQL Server遵循ANSI / ISO SQL-92规范,介绍如何将字符串与空格进行比较。 ANSI标准要求 填充比较中使用的字符串,以便在比较它们之前匹配它们的长度

更新我删除了我的代码 LIKE (在比较期间不填充空格)和 DATALENGTH() 因为它们对比较字符串并非万无一失

在许多其他地方也有人问过其他解决方案:


6
2017-10-15 04:06



我认为 DATALENGTH 我会在这里很好地工作,或存储计算 DATALENGTH 在一列中,以一个字节为代价使其更快。 - Matthew
这并没有回答所写的问题。它错误地声称这是关于这个问题的行为问题的重复 明确地 要求避免。 - Kenny Evitt


就像你说的,我不认为有很多选择。我能想出的唯一两个是这些:

DECLARE @x nvarchar(50)
DECLARE @y nvarchar(50)
SET @x = 'CAT     '
SET @y = 'CAT'

SELECT 1 WHERE len(@x + '_') = len(@y + '_')

SELECT 1 WHERE reverse(@x) = reverse(@y)

编辑

想到第三个:

SELECT 1 WHERE REPLACE(@x, ' ', '_') = REPLACE(@y, ' ', '_')

第四,假设您使用的是SQL 2005+

SELECT 1 WHERE QUOTENAME(@x) = QUOTENAME(@y)

就个人而言,我喜欢 reverse 想法最好,但这一切都取决于哪一个最适合你。


3
2017-10-15 00:36



REVERSE为+1,但LEN(foo + some char)可能是性能最高的! - p.campbell
@ p.campbell - 有趣,谢谢你指出来! - LittleBobbyTables
他们所有人都非常敏捷。根本没有好的解决方案。 - TomTom
reverse()用于万无一失的字符串比较,因为它在带有前导空格的字符串上失败。例如,'CAT'将与'.... CAT'“相等”(我无法插入空格,将点读取为空格) - Gennady Vanin Геннадий Ванин
@ vgv8 - 这是一个好点 - LittleBobbyTables


你可以尝试这样的东西:

declare @a varchar(10), @b varchar(10)
set @a='foo'
set @b='foo   '

select @a, @b, DATALENGTH(@a), DATALENGTH(@b)

2
2017-10-15 13:38



是的。 SELECT * FROM YourTable WHERE col = @searchterm and DATALENGTH(col) = DATALENGTH(@searchterm) 应该仍然是合理的sargable。 - Martin Smith
那真棒的马丁,我试图想办法做到这一点。 - DForck42


我只有两个建议。一种是重新审视需要您存储尾随空格的设计 - 在SQL中处理它们总是很痛苦。

第二个(给出您的SARG能力评论)将添加计算列到存储长度的表,并将此列添加到适当的索引。这样,至少,长度比较应该是SARG能力的。


1
2017-10-15 06:55



这个问题应该像杜普一样被关闭。之前在SO中进行了详尽而多次的回答,包括我在更新中给出的设计(截断尾随空格), stackoverflow.com/questions/1146280/... - Gennady Vanin Геннадий Ванин
@ vgv8 - 我认为这个略有不同,因为OP似乎在说它们必须存储尾随空格,并且它很重要。 - Damien_The_Unbeliever


经过一番搜索,我找到的最简单的解决方案是Anthony Bloesch 网络日志

只需在数据末尾添加一些文本(char足够)(追加)

SELECT 'foo' WHERE 'bar' + 'BOGUS_TXT' = 'bar    ' + 'BOGUS_TXT'

也适用于'WHERE IN'

SELECT <columnA>
FROM <tableA>
WHERE <columnA> + 'BOGUS_TXT' in ( SELECT <columnB> + 'BOGUS_TXT' FROM <tableB> )

1
2018-03-17 20:17





我打算使用的方法是使用正常的比较,该比较应该是索引可键控的(“sargable”),并由 DATALENGTH (因为 LEN 忽略空白)。它看起来像这样:

DECLARE @testValue VARCHAR(MAX) = 'x';

SELECT t.Id, t.Value
FROM dbo.MyTable t
WHERE t.Value = @testValue AND DATALENGTH(t.Value) = DATALENGTH(@testValue)

由查询优化器决定过滤器的顺序,但如果对正在测试的表有意义,则应选择使用索引进行数据查找,然后使用更昂贵的标量进一步过滤剩余的结果操作。但是,正如另一个答案所述,最好通过使用索引计算列来完全避免这些标量操作。如果您无法控制架构,或者您希望避免创建计算列,或者创建和维护计算列被认为比较差的查询性能更昂贵,则此处介绍的方法可能有意义。


1
2017-11-13 23:20