将UTF-16/Unicode数据存储在SQL Server中

7
根据这里的内容,SQL Server 2K5在内部使用UCS-2。可以使用适当的数据类型(如nchar等)在UCS-2中存储UTF-16数据,但如果有补充字符,则将其存储为2个UCS-2字符。
这带来了明显的字符串函数问题,即SQL Server将一个字符视为2个字符。
我有些惊讶于SQL Server基本上只能处理UCS-2,更令人惊讶的是,在SQL 2K8中没有解决这个问题。我确实理解这些字符可能并不常见。
除了文章中建议的函数外,还有什么建议来处理SQL Server 2K5中(损坏的)字符串函数和UTF-16数据的最佳方法吗?

有哪些字符串函数是出错的? - gbn
3
LEN函数返回字符串中UCS-2字符的数量,而不是UTF-16字符的数量。SUBSTRING函数会将UTF-16字符一分为二。LEFT和RIGHT函数同样如此。UPPER和LOWER函数也可能出现问题。REVERSE函数肯定会出错。CHARINDEX和PATINDEX函数也会出现问题。DIFFERENCE和STUFF函数也不确定。所以很多函数都会出现问题。 - David Cameron
2
感谢您指出这一点。它不支持所有Unicode字符的事实意味着一些UTF-16字符串值(例如来自Windows或.NET)在未经验证的情况下无法转储到SQL Server中。为了使任何应用程序无错误且技术上正确(造成错误的字符有多少并不重要,因为正确性是最重要的),在存储在SQL Server之前必须验证所有字符串以包含UCS-2兼容字符。太棒了!微软,你让我的工作更加困难了。 - Triynko
@Triynko 剥离除了0-65535之外的值是不必要、不合适,而且可能是愚蠢的。补充字符并不是唯一会表现出非直观行为的字符。在UCS-2中完全有效、可以正确排序和比较的组合字符,也会在许多/大多数内置字符串函数中出现问题。 - Solomon Rutzky
3个回答

9

虽然 SQL Server 2012 引入了 _SC 排序规则,可以正确处理补充字符,但问题非常具体,涉及到 SQL Server 2005。此外,“UTF-16 + 代理对”不是正确的说法,因为 UTF-16 = “UCS-2 + 代理对”。 - Solomon Rutzky
@SolomonRutzky,是的,这就是我说“包括”的原因。 - Concrete Gannet

3
字符串函数可以很好地处理Unicode字符字符串;关心字符数的函数将两个字节的字符视为单个字符,而不是两个字符。唯一需要注意的是len()和datalength(),在使用Unicode时它们返回不同的值。当然它们返回正确的值 - len()返回字符长度,datalength()返回字节长度。由于双字节字符,它们只是不同而已。
因此,只要在代码中使用正确的函数,一切都应该透明地运行。
编辑:刚刚双重检查了Books Online,自SQL Server 2000以来Unicode数据与字符串函数无缝工作。 编辑2: 如评论中指出,由于缺乏对平面0以外代理项的解析支持,SQL Server的字符串函数不支持完整的Unicode字符集(换句话说,SQL Server的字符串函数仅识别每个字符最多2个字节)。 SQL Server将正确存储和返回数据,但是任何依赖于字符计数的字符串函数都不会返回预期值。 最常见的绕过此问题的方法似乎要么在SQL Server之外处理字符串,要么使用CLR集成添加支持Unicode的字符串处理函数。

5
你误解了这个问题。UTF-16允许使用补充字符。这通过将一个字符(从用户的角度)存储在2个代码单元中,即4个字节来实现。UCS-2无法处理补充字符。因此,当实际上是一个字符时,SQL Server会将这4个字节视为两个字符处理。 - David Cameron
2
是的,但它不支持完整的Unicode字符集。 - David Cameron
1
我怀疑它坚持使用UCS-2而不是UTF-16的原因是UCS-2限制了自身的长度为两个字节,但除此之外与UTF-16完全相同。这使得UCS-2具有与UTF-16高度兼容性的特点,同时提供大小一致性,使char(8000字节)和nchar(4000字节)的最大大小更易于执行。尽管有任何坚持使用UCS-2而不是UTF-16的理由,但确实不支持代理对,因此不支持完整的Unicode字符集,这真的非常糟糕。 - Triynko
1
我想在评论中发表我的意见:这个答案是错误和误导的。SQL Server仅支持双字节字符。UTF-16有一些四字节字符。 - Concrete Gannet
@ConcreteGannet,“支持”是一个光谱。在非“_SC”排序中,对于UTF-16有一些支持,但非常有限。但是,“任何一个”排序都不能“适当地”处理合法的UCS-2 / BMP代码点的组合字符。例如:DECLARE @ Test NVARCHAR(10);SET @ Test = N'te'+NCHAR(0x0301)+ N'st';SELECT NCHAR(55357)+ NCHAR(56960)AS [WorksInAnyCollation],NCHAR(128640)AS [OnlyWorksIn_SC_Collations],@ Test AS [TestValue],LEN(@ Test)AS [Length],RIGHT(@ Test,3)AS [Oops];这两个NCHAR可以得到一个正确的补充字符。并且该问题并没有询问理想的支持。 - Solomon Rutzky
显示剩余8条评论

-2

有些需要补充的是,我刚刚通过艰难的方式学到:

如果你在 Oracle 中使用了“n”字段(我正在运行 9i 版本),并通过 .net oracleclient 访问它,似乎只有参数化 SQL 才能工作... N'字符串' Unicode 前缀似乎不能对一些内联 SQL 起作用。

而且所谓的“工作”,指的是:它将丢失任何不受基本字符集支持的字符。因此,在我的实例中,英文字符可以正常工作,而 Cyrillic 字符则会变成问号/垃圾字符。

这是关于该主题的更全面讨论:http://forums.oracle.com/forums/thread.jspa?threadID=376847

想知道 ORA_NCHAR_LITERAL_REPLACE 变量是否可以在连接字符串或其他地方设置。


嗨,boomhauer,这个问题是关于Microsoft SQL Server的。你的答案可能在其他地方有用。 - Concrete Gannet
哇...这里发生了什么事情。我是不是把帖子发错了?我几乎怀疑SO出了问题,因为它已经存在于2010年2月... - Brady Moritz
事实上,我知道这个答案曾经在另一个问题上! - Brady Moritz

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