如何将SQL Server Unicode / NVARCHAR字符串设置为表情符号或补充字符?


23

我想根据其Unicode代码点将Unicode字符串变量设置为特定字符。

我想使用65535以上的代码点,但是SQL Server 2008 R2数据库的排序规则为SQL_Latin1_General_CP1_CI_AS

根据Microsoft的NCHAR文档,该NCHAR函数采用一个整数,如下所示:

integer_expression

当数据库的排序规则不包含补充字符(SC)标志时,这是一个从0到65535(0到0xFFFF)的正整数。如果指定的值超出此范围,则返回NULL。有关补充字符的更多信息,请参见排序规则和Unicode支持。

当数据库的排序规则支持补充字符(SC)标志时,这是一个从0到1114111(从0到0x10FFFF)的正整数。如果指定的值超出此范围,则返回NULL。

所以这段代码:

SELECT NCHAR(128512);

NULL在此数据库中返回。

我希望它返回与此相同的内容:

SELECT N'😀';

在排序规则“不包含补充字符(SC)标志”的数据库中,如何使用代码(不使用实际的表情符号字符)将Unicode字符串变量(例如nvarchar)设置为表情符号?

表情符号Unicode代码点的完整列表

(最终,我希望任何角色都能正常工作。为了方便参考,我只是选择了表情符号。)

(尽管服务器是SQL Server 2008 R2,但我也对以后版本的任何解决方案感到好奇。)

假设没有办法,是否可以在另一个具有适当排序规则的数据库中引用内联用户定义函数?

如何找到带有“ supplementary character”标志的排序规则?

这不会在我们的服务器上返回任何记录:

SELECT * FROM sys.fn_helpcollations() 
WHERE name LIKE 'SQL%[_]SC';

似乎引入了SQL Server 2012 Latin1_General_100_CI_AS_SC可以正常工作。您可以在较旧的实例上安装排序规则吗?

整理参考:

是否有解释说明为什么SQL Server不管排序规则如何都可以理解和处理扩展字符(除了从角度来看之外)NCHAR


感谢您提供全面的其他信息。我不再面对这个问题,但我会在脑海中保留这些信息的书签。
莱利少校

1
没问题。我不认为您仍然需要一些东西,只是您可能会欣赏/能够利用这种改编...
所罗门·鲁兹基

Answers:


36

UCS-2编码始终为每个字符2个字节,范围为0-65535(0x0000-0xFFFF)。UTF-16(无论大端优先还是小端优先)的范围为0-1114111(0x0000-0x10FFFF)。UTF-16的0-65535 / 0x0000-0xFFFF范围是每个字符2个字节,而超过65536 / 0xFFFF的范围是每个字符4个字节。

Windows和SQL Server开始使用UCS-2编码,因为它可用并且UTF-16尚未完成。但是,幸运的是,在UCS-2和UTF-16的设计中已经有了足够的前瞻性,以至于UCS-2映射是UTF-16映射的完整子集(意味着:0-65535 / 0x0000-0xFFFF范围UTF-16 USC UCS-2)。AND,UTF-16的65536-1114111(0x10000-0x10FFFF)范围是由UCS-2范围内的两个代码点(特别是范围0xD800 – 0xDBFF和0xDC00 – 0xDFFF)构成的,否则没有含义。两个代码点的这种组合称为代理对,代理对表示UCS-2范围以外的字符,称为补充字符。

所有这些信息说明NVARCHAR了SQL Server中的/ Unicode数据的两个方面:

  1. 一些内置功能(不只是NCHAR())不使用增补字符感知归类时不处理代理项对/增补字符(SCA,即一个与_SC _140_,但没有_BIN*在名称),因为非SCA排序规则(尤其是SQL_排序规则)最初是在UTF-16完成之前实施的(我相信是在2000年的某个时候)。在名称或名称上没有,但在比较和排序方面对补充字符的支持最少的非SQL_归类。_90__100__SC
  2. 完整的Unicode / UTF-16字符集可以存储在NVARCHAR/ NCHAR/ XML/ NTEXT数据类型中,而不会造成任何数据丢失,因为UCS-2和UTF-16是完全相同的字节序列。唯一的区别是UTF-16利用代理代码点构造代理对,而UCS-2根本无法将它们映射到任何字符,因此它们在内置函数中显示为两个未知字符。

考虑到该背景信息,我们现在可以解决以下具体问题:

我想SELECT NCHAR(128512);返回与此相同的内容:SELECT N'😀';

仅当当前数据库(正在执行查询的数据库)具有默认字符排序规则(补充字符识别)并且在SQL Server 2012中引入了默认排序规则时,才会发生这种情况。具有字符串输入参数的内置函数可以提供排序规则通过内嵌的COLLATE条款(即LEN(N'string' COLLATE Some_Collation_SC)),并不需要将一个具有SCA缺省归类的数据库中执行。但是,诸如NCHAR()接受INT输入参数和COLLATE子句之类的内置函数在该上下文中无效(这就是为什么NCHAR()仅当当前数据库具有默认字符排序标准(支持补充字符)时才支持补充字符的原因;但这是不必要的不便之处可以更改,请对我的建议投赞成票:无论活动数据库的默认归类如何,NCHAR()函数应始终返回值0x10000-0x10FFFF的补充字符

是否有解释说明为什么SQL Server不管排序规则如何都可以理解和处理扩展字符(除了从角度来看之外)NCHAR

此答案的顶部解释了SQL Server如何存储和检索补充字符而不会丢失数据。但是,并不是NCHAR唯一的内置函数在补充字符方面存在问题(当不使用SCA排序规则时)。例如,LEN(N'😀' COLLATE SQL_Latin1_General_CP1_CI_AS)返回值2,而LEN(N'😀' COLLATE Latin1_General_100_CI_AS_SC)返回值1。

如果转到问题中发布的第二个链接(即“ Microsoft的补充字符归类信息”)并向下滚动一点,您将看到内置函数的图表以及它们基于有效归类的行为方式。

如何找到带有“ supplementary character”标志的排序规则?

在2012年之前的版本的SQL Server中,您不能这样做。但是,从SQL Server 2012开始,您可以使用以下查询:

SELECT col.*
FROM   sys.fn_helpcollations() col
WHERE  col.[name] LIKE N'%[_]SC'
OR     col.[name] LIKE N'%[_]SC[_]%'
OR     (COLLATIONPROPERTY(col.[name], 'Version') = 3
      AND col.[name] NOT LIKE N'%[_]BIN%');

您的查询已结束,但是模式始于,SQL而SQL Server排序规则(即以开头的模式SQL_)已弃用了一段时间,转而支持Windows排序规则(不是以开头的排序规则SQL_)。因此,SQL_归类不会更新,因此没有包含该_SC选项的较新版本(从SQL Server 2017开始,所有新归类均自动支持补充字符,不需要或没有_SC标志;是的,查询紧接在上方的内容说明了这一点,并说明了_UTF8在SQL Server 2019中添加的排序规则。

您可以在较旧的实例上安装排序规则吗?

不可以,您不能将排序规则安装到SQL Server的早期版本中。

在排序规则“不包含补充字符(SC)标志”的数据库中,如何使用代码(不使用实际的补充字符)将Unicode字符串变量(例如nvarchar)设置为补充字符?
...
尽管服务器是SQL Server 2008 R2,但我也对任何更高版本的解决方案感到好奇。

当不使用SCA归类时,可以通过两种方式注入高于65535 / U + FFFF的代码点:

  1. 根据两次调用该NCHAR()函数指定代理对,每个代理对中有一部分
  2. 根据转换VARBINARYLittle Endian(即反向)字节序列的形式指定代理对。

即使有效的归类是可识别补充字符的,这两种插入补充字符/代理对的方法也可以使用,并且在所有版本的SQL Server中都应相同,至少可以追溯到2005年(尽管在SQL Server 2000)。

例:

  • 字符:

                       💩

  • 名称:                便便堆
  • 小数:            128169
  • 码点:       U + 1F4A9
  • 代理对: U + D83D和U + DF21
SELECT N'💩', -- 💩
       UNICODE(N'💩' COLLATE Latin1_General_100_CI_AS), -- 55357
       UNICODE(N'💩' COLLATE Latin1_General_100_CI_AS_SC), -- 128169
       NCHAR(128169), -- 💩 in DB with _SC Collation, else NULL
       NCHAR(0x1F4A9), -- 💩 in DB with _SC Collation, else NULL
       CONVERT(VARBINARY(4), 128169), -- 0x0001F4A9
       CONVERT(VARBINARY(4), N'💩'), -- 0x3DD8A9DC
       CONVERT(NVARCHAR(10), 0x3DD8A9DC), -- 💩 (regardless of DB Collation)
       NCHAR(0xD83D) + NCHAR(0xDCA9) -- 💩 (regardless of DB Collation)

更新

您可以使用以下iTVF 从65536-1114111(0x010000-0x10FFFF)之间的任何代码点获取代理对值(包括形式INTBINARY形式)。并且,虽然输入参数的类型为INT,但您可以传递代码点的二进制/十六进制形式,它将隐式转换为正确的整数值。

CREATE FUNCTION dbo.GetSupplementaryCharacterInfo(@CodePoint INT)
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN

WITH calc AS
(
  SELECT 55232 + (@CodePoint / 1024) AS [HighSurrogateINT],
         56320 + (@CodePoint % 1024) AS [LowSurrogateINT]
  WHERE  @CodePoint BETWEEN  65536 AND 1114111
)
SELECT @CodePoint AS [CodePointINT],
       HighSurrogateINT,
       LowSurrogateINT,
       CONVERT(VARBINARY(3), @CodePoint) AS [CodePointBIN],
       CONVERT(BINARY(2), HighSurrogateINT) AS [HighSurrogateBIN],
       CONVERT(BINARY(2), LowSurrogateINT) AS [LowSurrogateBIN],
       CONVERT(binary(4), NCHAR(HighSurrogateINT) + NCHAR(LowSurrogateINT)) AS [UTF-16LE],
       NCHAR(HighSurrogateINT) + NCHAR(LowSurrogateINT) AS [Character]
FROM   calc;
GO

使用上面的函数,执行以下两个查询:

SELECT * FROM dbo.GetSupplementaryCharacterInfo(128169);

SELECT * FROM dbo.GetSupplementaryCharacterInfo(0x01F4A9);

都返回以下内容:

CodePoint  HighSurrogate  LowSurrgate  CodePoint  HighSurrgate  LowSurrgate  UTF-16LE   Char
INT        INT            INT          BIN        BIN           BIN                     actr
128169     55357          56489        0x01F4A9   0xD83D        0xDCA9       0x3DD8A9DC   💩

更新2:更好的更新!

我已经修改了上面显示的iTVF,现在可以返回188,657个代码点,因此您无需将其设置为任何特定值。当然,作为TVF,您可以添加一个WHERE子句以对特定代码点,代码点范围或“相似字符”等进行过滤。并且,它包括带有预格式化转义序列的其他列,以构造每个代码T-SQL,HTML和C样式(即\xHHHH)中的点(BMP和补充字符)。在这里阅读所有内容:

SSMS技巧3:轻松访问/研究所有Unicode字符(是,包括Emojis😸)


1
所罗门干得好!很棒的解释
罗尼·阿里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.