80

当我们必须使用 Unicode 类型时,是否有规则?

我已经看到大多数欧洲语言(德语、意大利语、英语……)在同一个数据库中的 VARCHAR 列中都很好。

我正在寻找类似的东西:

  1. 如果你有中文 --> 使用 NVARCHAR
  2. 如果你有德语和阿拉伯语 --> 使用 NVARCHAR

服务器/数据库的整理呢?

我不想像这里建议的那样总是使用 NVARCHAR varchar 和 nvarchar SQL Server 数据类型之间的主要性能差异是什么?

4

7 回答 7

124

您想要使用 NVARCHAR 的真正原因是当您在同一列中有不同的语言时,您需要在不解码的情况下在 T-SQL 中寻址列,您希望能够在 SSMS 中“本地”查看数据,或者您想要标准化 Unicode。

如果您将数据库视为哑存储,则完全可以在 VARCHAR(例如 UTF-8)中存储宽字符串和不同(甚至可变长度)编码。当您尝试编码和解码时,问题就出现了,尤其是当不同行的代码页不同时。这也意味着 SQL Server 将无法轻松处理数据,以便在 T-SQL 中查询(可能是可变的)编码列。

使用 NVARCHAR 可以避免这一切。

我会推荐 NVARCHAR 用于其中包含用户输入数据的任何列,这些数据相对不受约束。

我建议将 VARCHAR 用于任何作为自然键的列(如车牌、SSN、序列号、服务标签、订单号、机场呼号等),它通常由标准或立法或惯例定义和约束。VARCHAR 也用于用户输入,并且非常受限制(如电话号码)或代码(活动/关闭、Y/N、M/F、M/S/D/W 等)。绝对没有理由为这些使用 NVARCHAR。

所以对于一个简单的规则:

VARCHAR 当保证被约束时 NVARCHAR 否则

于 2009-03-05T20:44:44.217 回答
26

两个最受欢迎的答案都是错误的。它应该与“存储不同/多种语言”无关。您可以支持像ñ和英语这样的西班牙语字符,只使用公共varchar字段和Latin1_General_CI_AS COLLATION,例如

短版只要由字段确定的不支持所需的字符,就
应该使用NVARCHAR/ 。 此外,根据 SQL Server 版本,您可以使用特定的 ,例如自 SQL Server 2019 以来可用的。在字段(或整个表/数据库)上设置此排序规则,将用于存储和处理该字段上的数据,允许完全支持字符,因此它支持的任何语言。NCHARENCODINGCOLLATION
COLLATIONsLatin1_General_100_CI_AS_SC_UTF8VARCHARUTF-8 ENCODINGUNICODE


完全理解:
要完全理解我将要解释的内容,必须有 的概念UNICODEENCODING并且COLLATION所有这些概念都非常清楚。如果你不这样做,那么首先看看下面我对“什么是 UNICODE、ENCODING、COLLATION 和 UTF-8,以及它们是如何相关的”部分和提供的文档链接的简明扼要的解释。此外,我在这里所说的一切都特定于Microsoft SQL Server,以及它如何存储和处理char/ncharvarchar/nvarchar字段中的数据。

假设我们想在我们的 MSSQL Server 数据库中存储一个特殊的文本。它可能是一条 Instagram 评论,如“我喜欢 stackoverflow!”。
即使是 ASCII 也可以完美支持纯英文部分,但由于还有一个表情符号,它是UNICODE标准中指定的字符,我们需要一个ENCODING支持这个 Unicode 字符的字符。

MSSQL Server 使用COLLATION来确定在///字段ENCODING上使用什么。因此,与很多人认为的不同,不仅是关于排序和比较数据,而且是关于,因此:我们的数据将如何存储!charncharvarcharnvarcharCOLLATION ENCODING

那么,我们如何知道我们的校对使用的编码是什么?有了这个:

SELECT COLLATIONPROPERTY( 'Latin1_General_CI_AI' , 'CodePage' ) AS [CodePage]
--returns 1252

这个简单的 SQL 返回Windows Code Pagefor a COLLATION。AWindows Code Page只不过是另一个映射到ENCODINGs。对于Latin1_General_CI_AI COLLATION它返回的Windows Code Page代码1252,映射到Windows-1252 ENCODING.
因此,对于带有 的varchar列,Latin1_General_CI_AI COLLATION此字段将使用 处理其数据Windows-1252 ENCODING,并且仅正确存储此编码支持的字符。

如果我们检查 Windows-1252 的Windows-1252 ENCODING规范字符列表,我们会发现这种编码不支持我们的表情符号字符。如果我们仍然尝试一下:

包含 UNICODE 字符的文本,由于我们对 varchar 字段的排序和编码而被错误地存储

好的,那么我们如何解决这个问题?实际上,这取决于,这很好!

NCHAR/NVARCHAR

NCHAR在 SQL Server 2019 之前,我们只有NVARCHAR字段。有人说它们是UNICODE田地。那是错的!. 同样,它取决于字段COLLATION和 SQLServer 版本。微软的“nchar and nvarchar (Transact-SQL)”文档完美地说明了:

从 SQL Server 2012 (11.x) 开始,当使用启用了补充字符 (SC) 的排序规则时,这些数据类型存储所有 Unicode 字符数据并使用 UTF-16 字符编码。如果指定了非 SC 归类,则这些数据类型仅存储 UCS-2 字符编码支持的字符数据子集。

换句话说,如果我们使用比 2012 年更早的 SQL Server,例如 SQL Server 2008 R2,ENCODING那些字段将使用UCS-2 ENCODING支持UNICODE. 但是,如果我们使用 SQL Server 2012 或更新版本,并定义一个COLLATIONSupplementary Character启用的,那么我们的字段将使用UTF-16 ENCODING完全支持的UNICODE.


但是,还有更多!我们现在可以使用 UTF-8 了!!

CHAR/VARCHAR

从 SQL Server 2019 开始,我们可以使用CHAR/VARCHAR字段并且仍然完全支持UNICODE使用UTF-8 ENCODING!!!

来自微软的“char and varchar (Transact-SQL)”文档

从 SQL Server 2019 (15.x) 开始,当使用启用了 UTF-8 的排序规则时,这些数据类型存储全范围的 Unicode 字符数据并使用 UTF-8 字符编码。如果指定了非 UTF-8 归类,则这些数据类型仅存储该归类的相应代码页支持的字符子集。

再次,换句话说,如果我们使用比 2019 更旧的 SQL Server,例如 SQL Server 2008 R2,我们需要ENCODING使用前面解释的方法检查。但是,如果我们使用 SQL Server 2019 或更新版本,并定义一个COLLATIONlike Latin1_General_100_CI_AS_SC_UTF8,那么我们的字段将使用UTF-8 ENCODING迄今为止支持所有UNICODE字符的最常用和最有效的编码。


奖金信息:

关于 OP 对“我已经看到大多数欧洲语言(德语、意大利语、英语……)在 VARCHAR 列中的同一个数据库中都很好”的观察,我认为很高兴知道为什么会这样:

对于最常见的COLLATIONs,如默认的 as或Latin1_General_CI_AIwill用于字段。如果我们查看它的文档,我们可以看到它支持:SQL_Latin1_General_CP1_CI_ASENCODINGWindows-1252varchar

英语、爱尔兰语、意大利语、挪威语、葡萄牙语、西班牙语、瑞典语。另外还有德语、芬兰语和法语。和荷兰语,除了 IJ 字符

但正如我之前所说,这与语言无关,而与您希望支持/存储的字符有关,如表情符号示例所示,或者像“锂电池的电阻为 0.5Ω”这样的句子,我们又遇到了简单的英语和一个希腊字母/字符“omega”(这是电阻的符号,以欧姆为单位),不会被Windows-1252 ENCODING.

结论:

所以,就是这样!何时使用char/ncharvarchar/nvarchar取决于您想要支持的字符,以及您的 SQL Server 版本,该版本将确定COLLATIONsENCODINGs可以使用的字符。




什么是 UNICODE、ENCODING、COLLATION 和 UTF-8,以及它们之间的关系
注意:以下所有解释都是简化。请参阅提供的文档链接以了解有关这些概念的所有详细信息。

  • UNICODE- 是一种标准,一种约定,旨在规范统一有序的表格中的所有字符。在此表中,每个字符都有一个唯一编号。这个数字通常称为字符的code point
    UNICODE 不是编码!

  • ENCODING- 是字符和字节/字节序列之间的映射。因此,编码用于将字符“转换”为字节,反之亦然,从字节转换为字符。其中最受欢迎的是UTF-8,ISO-8859-1和。您可以将其视为“转换表”(我在这里确实简化了)。Windows-1252ASCII

  • COLLATION- 那个很重要。甚至微软的文档也没有明确说明这一点。排序规则指定您的数据将如何排序、比较和存储!. 是的,我敢打赌你没想到最后一个,对吧!?排序规则SQL Server也决定了ENCODING该特定char///字段上使用的内容ncharvarcharnvarchar

  • ASCII ENCODING- 是最早的编码之一。它既是字符表(就像一个自己的小版本UNICODE)和它的字节映射。所以它不会将一个字节映射到UNICODE,而是将一个字节映射到它自己的字符表。此外,它始终只使用 7 位,并支持 128 个不同的字符。足以支持所有英文字母大小写、数字、标点符号和其他一些有限数量的字符。ASCII 的问题在于,由于它只使用 7 位,而当时几乎每台计算机都是 8 位,因此还有另外 128 种可能的字符被“探索”,每个人都开始将这些“可用”字节映射到自己的字符表,创造了很多不同ENCODINGs

  • UTF-8 ENCODING- 这是另一个ENCODING,周围使用最多(如果不是最多)的一个ENCODING。它使用可变字节宽度(根据规范,一个字符的长度可以是 1 到 6 个字节)并完全支持所有UNICODE字符。

  • Windows-1252 ENCODING- 也是最常用ENCODING的一种,广泛用于 SQL Server。它是固定大小的,所以每个字符总是 1 个字节。它还支持很多口音,来自各种语言,但不支持所有现有的,也不支持UNICODE. 这就是为什么您的varchar字段具有常见排序规则的原因,例如Latin1_General_CI_ASsupport á, é, ñcharacters ,即使它没有使用 support UNICODE ENCODING

资源:
https ://blog.greglow.com/2019/07/25/sql-think-that-varchar-characters-if-so-think-again/
https://medium.com/@apiltamang/unicode-utf -8-and-ascii-encodings-made-easy-5bfbe3a1c45a
https://www.johndcook.com/blog/2019/09/09/how-utf-8-works/
https://www.w3.org/国际/问题/qa-what-is-encoding

https://en.wikipedia.org/wiki/List_of_Unicode_characters
https://www.fileformat.info/info/charset/windows-1252/list.htm

https://docs .microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/en-us/ sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/en-us/sql/t-sql/statements/windows-collat ​​ion-name-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/en -us/sql/t-sql/statements/sql-server-collat​​ion-name-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/en-us/sql/relational-databases/ collat​​ions/collat​​ion-and-unicode-support?view=sql-server-ver15#SQL-collat​​ions

SQL Server 默认字符编码
https://en.wikipedia.org/wiki/Windows_code_page

于 2020-08-28T16:54:22.970 回答
12

每当您必须存储多种语言时,都应该使用 NVARCHAR。我相信您必须将它用于亚洲语言,但不要引用我的话。

如果您以俄语为例并将其存储在 varchar 中,这就是问题所在,只要您定义正确的代码页就可以了。但是假设您使用默认的英文 sql 安装,那么俄文字符将无法正确处理。如果您使用的是 NVARCHAR() 它们将得到正确处理。

编辑

好的,让我引用MSDN,也许我是具体的,但你不想在 varcar 列中存储一个以上的代码页,虽然你可以你不应该

当您处理以 char、varchar、varchar(max) 或 text 数据类型存储的文本数据时,要考虑的最重要限制是系统只能验证来自单个代码页的信息。(您可以存储来自多个代码页的数据,但不建议这样做。)用于验证和存储数据的确切代码页取决于列的排序规则。如果尚未定义列级排序规则,则使用数据库的排序规则。要确定用于给定列的代码页,可以使用 COLLATIONPROPERTY 函数,如以下代码示例所示:

这里还有一些:

此示例说明了许多语言环境(例如格鲁吉亚语和印地语)没有代码页的事实,因为它们是仅 Unicode 排序规则。这些排序规则不适用于使用 char、varchar 或 text 数据类型的列

所以格鲁吉亚语或印地语确实需要存储为 nvarchar。阿拉伯语也是一个问题:

您可能遇到的另一个问题是,当您希望支持的所有字符都未包含在代码页中时,无法存储数据。在许多情况下,Windows 将特定的代码页视为“最合适”的代码页,这意味着不能保证您可以依赖该代码页来处理所有文本;它只是可用的最好的。阿拉伯文字就是一个例子:它支持多种语言,包括俾路支语、柏柏尔语、波斯语、克什米尔语、哈萨克语、吉尔吉斯语、普什图语、信德语、维吾尔语、乌尔都语等。除了 Windows 代码页 1256 中定义的阿拉伯语之外,所有这些语言都有其他字符。如果您尝试将这些额外字符存储在具有阿拉伯语排序规则的非 Unicode 列中,

使用 Unicode 时要记住的一点是,尽管您可以将不同的语言存储在单个列中,但您只能使用单个排序规则进行排序。有些语言使用拉丁字符,但不像其他拉丁语言那样排序。口音就是一个很好的例子,我不记得这个例子了,但是有一种东欧语言的 Y 不像英语 Y 排序。然后是西班牙用户要求在 h 之后排序的西班牙语 ch。

总而言之,在处理内部化时您必须处理的所有问题。我认为从一开始就使用 Unicode 字符更容易,避免额外的转换并占用空间。因此,我之前的声明。

于 2009-03-04T21:13:19.350 回答
4

希腊语在 N 列类型上需要 UTF-8:αβγ ;)

于 2009-03-04T21:11:23.583 回答
2

Josh 说:“....使用 Unicode 时要记住的一点是,尽管您可以将不同的语言存储在单个列中,但您只能使用单个排序规则进行排序。有些语言使用拉丁字符但排序不一样其他拉丁语言。口音就是一个很好的例子,我不记得这个例子了,但是有一种东欧语言的 Y 不像英语 Y 排序。然后是西班牙用户要求排序的西班牙语 ch在 h 之后。”

我是母语为西班牙语的人,“ch”不是一个字母,而是两个“c”和“h”,西班牙字母表是这样的:abcdefghijklmn ñ opqrstuvwxyz 我们不希望“ch”在“h”之后,而是“i”除了 ñ 或 HTML 中的“ñ ;”之外,字母表与英语中的字母表相同

亚历克斯

于 2009-05-04T06:15:30.230 回答
0

TL;博士;
Unicode -(nchar、nvarchar 和 ntext)
非 unicode -(char、varchar 和 text)。

来自 MSDN

SQL Server 中的排序规则为您的数据提供排序规则、区分大小写和区分重音的属性。与字符数据类型(如 char 和 varchar)一起使用的排序规则规定了代码页和可以为该数据类型表示的相应字符。

假设您使用的是默认 SQL 排序规则SQL_Latin1_General_CP1_CI_AS,那么下面的脚本应该打印出您可以放入的所有符号,VARCHAR因为如果您在打印的列表中看不到它,它使用一个字节来存储一个字符(总共 256 个) - 您需要NVARCHAR.

declare @i int = 0;
while (@i < 256)
begin
print cast(@i as varchar(3)) + '  '+  char(@i)  collate SQL_Latin1_General_CP1_CI_AS 
print cast(@i as varchar(3)) + '  '+ char(@i)  collate Japanese_90_CI_AS  
set @i = @i+1;
end

如果您将排序规则更改为日语,您会注意到所有奇怪的欧洲字母都变成了正常的,一些符号变成了?标记。

Unicode 是将代码点映射到字符的标准。因为它旨在涵盖世界上所有语言的所有字符,所以不需要不同的代码页来处理不同的字符集。如果存储反映多种语言的字符数据,请始终使用 Unicode 数据类型(nchar、nvarchar 和 ntext)而不是非 Unicode 数据类型(char、varchar 和 text)。

否则你的排序会变得很奇怪。

于 2016-03-23T15:22:15.717 回答
0

如果有人在 Mysql 中遇到此问题,则无需将 varchar 更改为 nvarchar 您只需将列的排序规则更改为 utf8

于 2019-11-26T10:19:32.060 回答