Shi*_*iva 18 sql-server datatypes character-set encoding unicode
我有一个带有 varchar 列的表。它允许使用商标 (™)、版权 (©) 和其他 Unicode 字符,如下所示。
Create table VarcharUnicodeCheck
(
col1 varchar(100)
)
insert into VarcharUnicodeCheck (col1) values ('MyCompany')
insert into VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into VarcharUnicodeCheck (col1) values ('MyCompany?')
insert into VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into VarcharUnicodeCheck (col1) values ('MyCompany')
select * from VarcharUnicodeCheck
Run Code Online (Sandbox Code Playgroud)
但是varchar的定义说,它允许非 unicode 字符串数据。但 Trademark(™) 和 Registered(®) 符号是Unicode字符。定义是否与 varchar 数据类型的属性相矛盾?我阅读了几个链接,例如第一个和第二个。但是我仍然不明白为什么当定义说它只允许非 unicode 字符串值时它允许 unicode 字符串。
sep*_*pic 14
但 Trademark(™) 和 Registered(®) 符号是 Unicode 字符。
你错在这里。您的字符串仅包含 ascii 字符。
这是一个简单的测试,显示您的字符都是 ascii(+ 一些扩展的 ascii,ASCII 代码介于 128 和 255 之间):
declare @VarcharUnicodeCheck table
(
col1 varchar(100)
)
insert into @VarcharUnicodeCheck (col1) values ('MyCompany')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany?')
insert into @VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany')
select *,
right(col1, 1)as last_char,
ascii(right(col1, 1)) as_last_char_ascii
from @VarcharUnicodeCheck;
Run Code Online (Sandbox Code Playgroud)
在这里您可以清楚地看到所有字符都是 1 字节编码的:
是的,它们不是纯 ASCII 字符,而是扩展 ASCII 字符。
在这里,我向您展示真正的 Unicode 字符Trademark(™)
及其代码和二进制表示:
declare @t table (uni_ch nchar(1), ascii_ch char(1));
insert into @t values (N'™', '™');
select unicode(uni_ch) as [unicode of ™],
ascii(ascii_ch) [ascii of ™],
cast(uni_ch as varbinary(10)) as [uni_ch as varbinary],
cast(ascii_ch as varbinary(10)) as [ascii_ch as varbinary]
from @t;
Run Code Online (Sandbox Code Playgroud)
最后,您可以看到 Trademark(™) Unicode 字符具有 8482 代码而不是 153:
select nchar(8482), nchar(153)
Run Code Online (Sandbox Code Playgroud)
从评论中,我同意“扩展 ASCII”是一个非常糟糕的术语,它实际上意味着一个代码页,它映射 128-255 范围内的字符/代码点,超出了 ASCII 定义的标准 0-127 代码点范围。
SQL Server 通过排序规则支持许多代码页。只要底层排序规则支持该字符,非 ASCII 字符就可以存储在 varchar 中。
当 SQL Server 归类代码页为 1250 或更大时,“™”字符可以存储在 varchar/char 列中。下面的查询将列出这些:
SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') >= 1250
ORDER BY name;
Run Code Online (Sandbox Code Playgroud)
但只有其中的一个子集也支持 '©' 字符,因此列排序规则需要是以下之一才能支持两者:
SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') IN(
1250
,1251
,1252
,1253
,1254
,1255
,1256
,1257
,1258
)
ORDER BY name;
Run Code Online (Sandbox Code Playgroud)
但是 varchar 的定义说,它允许 non-unicode string data。但 Trademark(™) 和 Registered(®) 符号是 Unicode字符。定义是否与 varchar 数据类型的属性相矛盾?
虽然其他答案并没有错,但我认为指出基本术语的混淆会有所帮助。我在上面引用的问题中强调了两个词,作为这种混淆的一个例子。当 SQL Server 文档谈到 Unicode 和非 Unicode数据时,他们并不是在谈论字符。他们说的是代表某些字符的字节序列。Unicode的类型(之间的主要差别NCHAR
,NVARCHAR
,XML
,和弃用/恶NTEXT
)和非Unicode类型(CHAR
,VARCHAR
以及弃用/恶TEXT
)是什么类型的字节序列的他们可以存储。
非 Unicode 类型存储几种 8 位编码之一,而 Unicode 类型存储单个 16 位 Unicode 编码:UTF-16 Little Endian。正如其他答案所提到的,哪些字符可以存储在 8 位/非 Unicode 编码中取决于代码页,代码页由排序规则确定。虽然其他人已经注意到“字符”的字节值可以在它所在的代码页之间变化,但在处理几个 EBCDIC 代码页之一(Windows 的变体)时,字节值甚至可以在同一代码页内变化。 1252),仅在较旧的版本中找到,不应真正使用 SQL Server 排序规则(即名称以 开头的排序规则SQL_
)。
因此,定义是准确的:您可以设法存储在非 Unicode 类型中的任何字符始终为 8 位(即使它们组合使用两个 8 位值作为单个“字符”,这就是 Double-字节字符集/DBCS 代码页允许)。并且 Unicode 数据类型始终是 16 位,即使它们有时将两个 16 位值组合用作单个“字符”(即代理对,它依次代表一个补充字符)。
VARCHAR
和CHAR
数据类型,VARCHAR
不能再被称为“非 Unicode”。因此,从 2018 年 9 月 SQL Server 2019 的第一个公开测试版开始,我们应该将其VARCHAR
称为“8 位数据类型”,即使在使用 SQL Server 2019 之前的版本时也是如此。该术语适用于所有 4 种类型可以与VARCHAR
以下一起使用的编码:
只有TEXT
数据类型(从 SQL Server 2005 开始不推荐使用,所以不要使用它)是“非 Unicode”,但这只是一个技术问题,将其称为“8 位数据类型”是准确的。
NVARCHAR
, NCHAR
, 并且NTEXT
可以称为“UTF-16”或“16 位数据类型”。我相信 Oracle 使用“仅限 Unicode”的术语NVARCHAR
,但这并不能明确排除使用 UTF-8(也是 Unicode 编码)的可能性,因为它不起作用,所以最好坚持使用前两个选项。
有关新的 UTF-8 编码的详细信息,请参阅我的帖子:
SQL Server 2019 中的原生 UTF-8 支持:救世主还是假先知?
PS 我正在慢慢地更新 SQL Server 文档以反映这些更改。
PPS Microsoft 已经使用 UTF-8 信息更新了一些页面,包括问题中引用的char 和 varchar文档。它不再包含短语“非 Unicode”。但这只是一个仅供参考;它不会改变问题,因为这是关于包含被错误地认为是仅 Unicode 的字符的非 Unicode 编码。