什么时候必须在SQL Server中使用NVARCHAR / NCHAR而不是VARCHAR / CHAR?


69

我们必须使用Unicode类型时有规则吗?

我已经看到,大多数欧洲语言(德语,意大利语,英语等等)在VARCHAR列的同一数据库中都可以。

我正在寻找类似的东西:

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

服务器/数据库的整理如何?

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


两个最被推崇的问题都是错误的。它与“存储不同/多种语言”无关。您可以ñ使用常见的varchar支持西班牙语和英语等西班牙语字符。
Vitox

我刚刚发布了一个答案,解释并提供了有关如何处理它的更新方法。
Vitox

Answers:


5

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

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


要充分认识到:
要完全明白我做一下解释,这是强制性的,以拥有的概念UNICODEENCODINGCOLLATION在你的脑袋都非常清楚了。如果您不这样做,那么首先请看下面关于“什么是UNICODE,编码,集合和UTF-8,以及它们之间的关系”的简明扼要的解释以及随附的文档链接。另外,我在这里说的所有内容都是特定于的Microsoft SQL Server,以及它如何存储和处理char/ncharvarchar/nvarchar字段中的数据。

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

MSSQL Server使用COLLATION,以确定哪些ENCODING是在使用char/ nchar/ varchar/nvarchar场。因此,与很多人不同的COLLATION 是,这不仅与数据的排序和比较有关ENCODING,而且与结果有关:如何存储我们的数据!

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

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

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

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

包含UNICODE字符的文本,由于我们在varchar字段上进行了归类和编码,因此错误地存储了该文本

好,那我们该如何解决呢?实际上,这取决于,那就好!

NCHAR/NVARCHAR

在SQL Server 2019之前,我们所拥有的是NCHARandNVARCHAR字段。有人说它们是UNICODE田野。那是错的!。同样,它取决于字段的类型COLLATION以及SQLServer的版本。微软的“ nchar和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!!!

来自Microsoft的“ char和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,如默认字段asLatin1_General_CI_AISQL_Latin1_General_CP1_CI_AStheENCODINGWindows-1252用于varchar字段。如果我们看一下它的文档,我们可以看到它支持:

英文,爱尔兰文,意大利文,挪威文,葡萄牙文,西班牙文,瑞典文。加上德语,芬兰语和法语。和荷兰语,除了IJ字符

但是正如我之前说的,这与语言无关,与表情符号示例中显示的是您期望支持/存储的字符有关,或者诸如“锂电池的电阻为0.5Ω”之类的句子普通英语,以及希腊字母/字符“ omega”(以欧姆为单位的电阻符号),而不会正确处理Windows-1252 ENCODING

结论:

就是这样!使用char/ncharvarchar/的时间nvarchar取决于您要支持的字符,还取决于SQL Server的版本,SQL Server的版本将确定可用的字符,COLLATIONs从而确定ENCODINGs可用的字符。




什么是UNICODE,ENCODING,COLLATION和UTF-8,以及它们之间的相关性
注意:以下所有说明均为简化。请参考提供的文档链接以了解有关这些概念的所有详细信息。

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

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

  • COLLATION-那很重要。甚至Microsoft的文档也没有像应该这样澄清。排序规则指定了如何对数据进行排序,比较和存储!。是的,我敢打赌您没想到最后一个,对吧!在归类SQL Server决定过这将是ENCODING用在特定char/ nchar/ varchar/nvarchar场。

  • 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_AS支持áéñ即使它没有使用支持字符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/ International / questions / 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 / zh-CN / sql / t-sql / data-types / char-and-varchar-transact-sql?view = sql-server-ver15
https://docs.microsoft.com/zh-CN/ sql / t-sql / data-types / nchar-and-nvarchar-transact-sql?view = sql-server-ver15
https://docs.microsoft.com/zh-CN/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/zh-CN/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


116

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

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

使用NVARCHAR避免了所有这些情况。

对于任何将用户输入的数据相对不受限制的列,我建议使用NVARCHAR。

我建议将VARCHAR用于任何自然键(例如,车牌,SSN,序列号,服务标签,订单号,机场呼号等),该列通常受标准或法律或公约的约束。也是用户输入的VARCHAR,非常受限制(例如电话号码)或代码(有效/关闭,是/否,M / F,M / S / D / W等)。绝对没有理由使用NVARCHAR。

因此,一个简单的规则是:

保证受约束时为VARCHAR否则为NVARCHAR


5
应当注意的是,“不同的语言”不仅仅意味着不同的行可以包含来自不同语言的值。这也意味着数据库的默认排序规则(即服务器计算机的语言环境)是否与任何客户端计算机的语言环境不同。例如,服务器计算机设置为en-US,但我的PC设置为fr-US
伊恩·博伊德

@IanBoyd通常,在将列中的语言混合并在一个集合中返回多种语言的项目并使用该排序规则进行排序时,排序规则将存在很大问题。核对也可对被组合以被视为一个(匈牙利DZ和LY)字符的效果:sqlservercentral.com/Forums/Topic19439-9-1.aspx stackoverflow.com/questions/7207590/... - nvarchar的是不会解决这个问题
Cade Roux 2012年

3
许多亚洲国家(包括中国)在其车牌中使用徽标,因此,除非您能百分百确定自己的程序绝对不会而且永远不会处理此类数据,否则最好将nvarchar用作车牌。是的,其中包括违反交通法规的人员登记,停车场和车辆运输方式。来自中国的某人完全有可能乘渡轮甚至开车去您的国家,并将他们的汽车停放在您的车库中。
Nzall 2014年

这是不正确的:it is perfectly possible to store wide strings and different (even variable-length) encodings in VARCHAR (for instance UTF-8)。您不能存储列编码不支持的字符。如果该列具有Latin1_General_CI_AS排序规则,它将带有Windows-1252encoling,因此您将无法存储表情符号,希腊字母Ω示例字符。Windows-1252该字节序列的编码中没有正确的映射。的将是一个为存储?,并且Ω将被转换为O
Vitox

11

您必须在必须存储多种语言的任何时候使用NVARCHAR。我相信您必须将其用于亚洲语言,但不要在上面引用我的意思。

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

编辑

好吧,让我引述MSDN,也许我只是特定的,但是您不想在varcar列中存储一个以上的代码页,而您可以

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

还有更多:

此示例说明了以下事实,例如格鲁吉亚语和北印度语等许多语言环境没有代码页,因为它们是仅Unicode归类。这些归类不适用于使用char,varchar或text数据类型的列

因此,格鲁吉亚语或北印度语确实需要存储为nvarchar。阿拉伯语也是一个问题:

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

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

总而言之,您在处理内部分类时必须处理的所有问题。我认为,从一开始就使用Unicode字符会更容易,避免额外的转换并节省空间。因此,我的发言较早。


4
>>必须在需要存储多种语言的任何时候使用NVARCHAR,这是不正确的。德语,意大利语和英语非常适合使用VARCHAR列的同一表。请再具体些
彼得Gfader



2

乔什说:“ ....使用Unicode时要记住的一点是,尽管可以将不同的语言存储在单个列中,但只能使用单个排序规则进行排序。有些语言使用拉丁字符,但排序不像口音就是一个很好的例子,我无法说明这个例子,但是有一种东欧语言,其Y排序不像英语Y。在h之后。”

我是一个以西班牙语为母语的人,“ ch”不是字母,而是两个“ c”和“ h”,西班牙语字母像:abcdefghijklmn-opqrstuvwxyz我们不希望在“ h”之后出现“ ch”,而希望“ i”除了ñ或HTML“ñ”外,该字母与英语相同。

亚历克斯


嗨,Alex,您曾经在1列中存储过不同的语言吗?1个表格中有不同语言的不同列。
Peter Gfader,2009年

他们可能在引用捷克语。我们在“ h”和“ i”之间使用“ ch”,这是一个单独的字母。
jahav 2015年


0

TL; DR;
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)。

否则,您的排序将很奇怪。


“ Unicode-(nchar,nvarchar和ntext)。非unicode-(char,varchar和text)。” 这是不正确的。nchar/在不同版本的SQL Server中nvarchar使用不同encodings的功能,例如UCS-2不完全支持UNICODE
Vitox
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.