我有一个带有 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
但是varchar 的定义说,它允许非 unicode 字符串数据。但是 Trademark(™) 和 Registered(®) 符号是Unicode字符。定义是否与 varchar 数据类型的属性相矛盾?我读了几个链接,比如第一个和第二个。但是当定义说它只允许非 unicode 字符串值时,我仍然不明白为什么它允许 unicode 字符串。
你在这里错了。您的字符串仅包含 ascii 字符。
这是一个简单的测试,显示您的字符都是 ascii(+ 一些扩展 ascii,ascii 代码在 128 到 255 之间):
在这里您可以清楚地看到您的所有字符都是 1 字节编码的:
是的,它们不是纯 ascii 字符,但它们是Extended ASCII。
在这里,我向您展示真正的 Unicode 字符
Trademark(™)
及其代码和二进制表示:最后,您可以看到 Trademark(™) Unicode 字符的代码是 8482 而不是 153:
从评论中,我同意“扩展 ASCII”是一个非常糟糕的术语,它实际上意味着一个代码页,它映射了 128-255 范围内的字符/代码点,超出了 ASCII 定义的标准 0-127 代码点范围。
SQL Server 通过排序规则支持许多代码页。只要底层排序规则支持该字符,非 ASCII 字符就可以存储在 varchar 中。
当 SQL Server 排序规则代码页为 1250 或更大时,“™”字符可以存储在 varchar/char 列中。下面的查询将列出这些:
但其中只有一个子集也支持“©”字符,因此列排序规则需要是以下之一以支持两者:
虽然其他答案并不正确,但我认为指出基本术语的混淆会有所帮助。我在上面引用的问题中强调了两个词,作为这种混淆的一个例子。当 SQL Server 文档谈到 Unicode 和非 Unicode数据时,它们并不是在谈论字符。他们说的是代表某些字符的字节序列。
NCHAR
Unicode 类型( 、NVARCHAR
、XML
和不推荐使用的 / evilNTEXT
)和非 Unicode 类型(CHAR
、VARCHAR
和不推荐使用的 / evilTEXT
)之间的主要区别在于它们可以存储的字节序列类型。非 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 位值组合用作单个“字符”(即,代理对又代表补充字符)。
并且,由于 SQL Server 本身支持 SQL Server 2019 的 UTF-8 编码
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文档。它不再包含短语“non-Unicode”。但这只是一个仅供参考;它不会改变问题,因为这是关于非 Unicode 编码,其中包含被错误地认为是仅 Unicode 的字符。
这个问题包含一个关于 Unicode 是什么的核心误解。Unicode 字符集及其编码(如 UTF-8 和 UTF-16)是在计算机中表示文本的多种方式之一,其目的是取代所有其他字符集和编码。如果“非 Unicode 数据”的意思是“Unicode 中不存在的字符”,那么我在这个答案中使用的所有文本都不能存储在该类型中,因为拉丁字母的所有字母和日常英语中使用的常见标点符号都是包含在 Unicode 中。
文本表示可以大致分为两部分:将不同字符(字母、数字、符号等)映射到参考图表上的数字的字符集;以及将这些数字表示为位模式的编码(在磁盘上、通过网络连接等)。在这里,我们主要关注第一部分:特定字符集的图表中列出了哪些字符。
由于 Unicode 旨在为世界上的每个字符提供数字(它称为“代码点”),因此像 Wikipedia 这样的参考资料通常会将字符的 Unicode 位置作为参考信息的标准片段。但是,这并不意味着其他字符集也没有相同字符的映射。
仍在使用的最古老和最简单的字符集(和编码)之一是 ASCII,它具有 128 个不同字符(0 到 127)的映射,因为它使用 7 位来编码每个字符。由于这排除了许多重音字符和常见符号,因此后来的编码使用 8 位,并映射相同的前 128 个字符,通过填充位置 128 到 255 添加到字符集。其中值得注意的是标准ISO 8859-1和ISO 8859- 15和 Microsoft 特定的Windows 代码页 1252。
因此,回到 MS SQL Server:存储在 、 或 列中的“Unicode 字符串”
nchar
可以nvarchar
表示ntext
映射到 Unicode 字符集中的所有字符,因为它使用 Unicode 编码来存储数据。char
存储在、varchar
或列中的“非 Unicode 字符串”text
只能表示以其他编码映射的字符。您可以存储在非 Unicode 列中的任何内容也可以存储在 Unicode 列中,但反之则不然。要确切知道可以存储哪些字符,您需要知道使用中的“排序规则”,它规定了 Microsoft 所称的“代码页”,如Microsoft 参考页中所述。在您的情况下,您可能正在使用我之前提到的非常常见的代码页 1252。
您提到的字符同时存在于 Unicode 和 Code Page 1252 中:
接受某事和只接受某事不是一回事。如果你去一个说“我们接受 50 美元的钞票”的得来速餐厅,这是否意味着他们只接受 50 美元的钞票?当然不是。同样,接受 Unicode 和只接受 Unicode 也不相同。您添加了“仅”一词,根据您的问题,它在您引用的定义中不存在。