.NET和T-SQL之间的字符串比较差异?

13
在我编写的一个测试用例中,字符串比较似乎在 SQL Server 和 .NET CLR 之间不以相同的方式工作。
这是C#代码:
string lesser =  "SR2-A1-10-90";
string greater = "SR2-A1-100-10";

Debug.WriteLine(string.Compare("A","B"));
Debug.WriteLine(string.Compare(lesser, greater));

将输出:

-1
1

这段 SQL Server 代码:

declare @lesser varchar(20);
declare @greater varchar(20);

set @lesser =  'SR2-A1-10-90';
set @greater = 'SR2-A1-100-10';

IF @lesser < @greater
    SELECT 'Less Than';
ELSE
    SELECT 'Greater than';

将输出:

Less Than

为什么会有差异?


你是否考虑了大小写敏感性? - user114600
这些情况是相同的。不同之处在于每个如何处理“0”到“-”之间的差异。 - Matt Brunell
这可能是一个Unicode问题。编辑:也许不是。对我来说,SQL Server代码返回“大于”。你测试的数据库中默认的排序规则是什么? - Martin Smith
1
如果 IF @lesser < @greater collate Latin1_General_CI_AS,则返回 greater than。对于我来说,如果 IF @lesser < @greater collate SQL_Latin1_General_CP1_CI_AS,则返回 less than - Martin Smith
好的,是的。如果我手动指定排序规则,我会得到相同的结果。 - Matt Brunell
4个回答

10

这里有详细说明

Windows排序规则(例如Latin1_General_CI_AS)使用Unicode类型的排序规则。而SQL排序规则不是这样。

这会导致连字符在两者之间处理方式不同。


选择 * 从 fn_helpcollations() 其中 name like '%SQL_Latin1_General_CP1_CI_AS%' 或者 name = 'Latin1_General_CI_AS' - gbn
非 Unicode 排序方式不同,使用 CP 1252。我确定 LATIN1.. 也是这样做的... 不,它不是... http://msdn.microsoft.com/en-us/library/ms143515.aspx - gbn
只是为了添加一个文档页面,因为我在这里已经好几年了。https://learn.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver16 - Pimenta

7
进一步解释gbn的回答,您可以在C#中使用CompareOptions.StringSort(或使用StringComparison.Ordinal)使它们表现相同。这将把符号视为在字母数字符号之前出现,因此“-” < “0”。
然而,Unicode与ASCII并不能说明任何问题,因为ASCII代码页的十六进制代码直接翻译为Unicode代码页: “-”是002D(45),而“0”是0030(48)。
发生的情况是.NET默认使用“语言”排序,该排序基于指定或当前文化对各种符号应用的非序数排序和权重。例如,“résumé”(带重音符号拼写)紧随不带重音符号拼写的“resume”在排序的单词列表中立即出现,因为“é”在“e”之后给出了一个分数顺序,并且在“f”之前。它还允许将“合作”和“合作”放在一起,因为破折号符号被赋予低“权重”,仅在像“位”,“位”的排序时才具有绝对的最终关键词汇(这些词汇按顺序排列:“位”,“位的”和“位移”)。
所谓的序数排序(根据Unicode值严格排序,可以大小写不敏感)将产生非常不同甚至是不合逻辑的结果,因为字母的变体通常出现在ASCII / Unicode序数中基本未装饰的拉丁字母表之后,而符号则在其之前。例如,“é”出现在“z”之后,因此单词“resume”,“rosin”,“ruble”,“résumé”将按该顺序排序。“Bit's”,“Bit-shift”,“Biter”,“Bits”将按该顺序排序,因为撇号先出现,然后是破折号,然后是字母“e”,然后是字母“s”。从“自然语言”角度来看,这两者都不合理。

代码页只影响大于127的字符,是吗? - gbn
很棒。通过使用CompareOption.StringSort,我可以在当前数据库排序设置下运行我的测试。 - Matt Brunell
@gbn - 从技术上讲,是的。似乎也是C#在排序时出了问题;没有StringSort,符号字符会排在字母数字字符之后。我会编辑一下。 - KeithS

3
  • 在SQL中,你使用的是varchar,它基本上是ASCII编码(受排序规则影响),这将在0之前给出“-”。
  • 在C#中,所有字符串都是Unicode编码。

UTF-xx(c#)与UCS-2(SQL Server)的细微差别非常棘手。

编辑:

我发帖太快了。

我在SQL Server 2008上使用Latin1_General_CI_AS排序规则得到了“大于”符号。

编辑2:

我也会尝试在破折号上使用SELECT ASCII(...)。例如,如果SQL片段曾经在Word文档中出现过,“-”(150)不是我从你的问题复制到浏览器中进行测试后在SQL Server中复制的“-”(45)。请参见CP 1252(= CP1 = SQL Server术语)。

编辑3:请参见Martin Smith的答案:两个排序规则具有不同的排序顺序。


啊,我明白了。当我使用nvarchar(20)时,我得到“大于”。 - Matt Brunell
我使用varchar得到这个结果。你的数据库排序规则与服务器排序规则不同吗? - gbn

1

已经有几个很好的答案解释了为什么会发生这种情况,但我相信其他人只想知道C#代码如何按照与SQL Server相同的顺序迭代集合。我发现以下代码最有效。 "Ordinal"可以解决连字符问题,而"IgnoreCase"似乎也反映了SQL Server的默认设置。

Debug.WriteLine(string.Compare(lesser, greater, StringComparison.OrdinalIgnoreCase));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接