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数据的两个方面:
- 一些内置功能(不只是
NCHAR()
)不使用增补字符感知归类时不处理代理项对/增补字符(SCA,即一个与_SC
,或 _140_
,但没有_BIN*
在名称),因为非SCA排序规则(尤其是SQL_
排序规则)最初是在UTF-16完成之前实施的(我相信是在2000年的某个时候)。在名称或名称上没有,但在比较和排序方面对补充字符的支持最少的非SQL_
归类。_90_
_100_
_SC
- 完整的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的代码点:
- 根据两次调用该
NCHAR()
函数指定代理对,每个代理对中有一部分
- 根据转换
VARBINARY
Little 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)之间的任何代码点获取代理对值(包括形式INT
和BINARY
形式)。并且,虽然输入参数的类型为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😸)