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

时间:2009-03-04 21:09:20

标签: sql-server unicode collation nvarchar

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

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

我正在寻找类似的东西:

  1. 如果你有中文 - >使用NVARCHAR
  2. 如果你有德语和阿拉伯语 - >使用NVARCHAR
  3. 服务器/数据库的整理怎么样?

    我不想像这里建议的那样使用NVARCHAR What are the main performance differences between varchar and nvarchar SQL Server data types?

7 个答案:

答案 0 :(得分:108)

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

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

使用NVARCHAR可以避免这一切。

我建议NVARCHAR用于任何具有用户输入数据的列,该列相对不受约束。

我建议将VARCHAR用于任何自然键列(如车牌,SSN,序列号,服务标签,订单号,机场呼号等),这些列通常由标准或法规定义和约束或惯例。此外,VARCHAR用于用户输入,非常受约束(如电话号码)或代码(ACTIVE / CLOSED,Y / N,M / F,M / S / D / W等)。绝对没有理由使用NVARCHAR。

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

保证受约束时

VARCHAR NVARCHAR否则

答案 1 :(得分:10)

您必须在任何时候存储多种语言时使用NVARCHAR。我相信你必须将它用于亚洲语言,但不要引用我。

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

修改

好吧,让我引用MSDN,也许我是特定的,但你不想在varcar列中存储多个代码页,而你可以不应该

  

处理文本数据时   存储在char,varchar中,   varchar(max)或文本数据类型   最重要的考虑因素   只是来自单一的信息   代码页可以通过验证   系统。 (您可以存储来自的数据   多个代码页,但事实并非如此   推荐。)使用的确切代码页   验证和存储数据取决于   关于列的整理。如果一个   列级排序规则尚未完成   定义,数据库的整理   用来。确定代码页   用于给定列的,你   可以使用COLLATIONPROPERTY   功能,如下所示   代码示例:

还有一些:

  

这个例子说明了这个事实   许多语言环境,例如格鲁吉亚语和   印地语,没有代码页,因为他们   是仅限Unicode的排序规则。那些   整理不适合   使用char,varchar或的列   文本数据类型

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

  

您可能遇到的另一个问题是   没有时无法存储数据   所有你想要的角色   支持包含在代码中   页。在很多情况下,Windows会考虑   一个特定的代码页是“最好的   适合“代码页,这意味着有   不能保证你可以依赖   用于处理所有文本的代码页;它是   只是最好的一个。一个   这方面的例子是阿拉伯语脚本:   它支持多种语言,   包括俾路支,柏柏尔人,波斯人,   克什米尔,哈萨克斯坦,吉尔吉斯,普什图语,   信德,维吾尔,乌尔都语等。所有的   这些语言有额外的   超出阿拉伯语的字符   Windows代码中定义的语言   第1256页。如果您尝试存储   这些额外的角色   具有阿拉伯语的非Unicode列   整理,人物是   转换成问号。

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

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

答案 2 :(得分:5)

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

简短版本
每当由字段NVARCHAR确定的NCHAR不支持所需的字符时,应使用ENCODING / COLLATION
另外,根据SQL Server版本的不同,您可以使用特定的COLLATIONs,例如Latin1_General_100_CI_AS_SC_UTF8,自SQL Server 2019起可用。在VARCHAR字段(或整个表/数据库)上设置此排序规则),将使用UTF-8 ENCODING来存储和处理该字段上的数据,从而完全支持UNICODE字符,并因此支持它所包含的任何语言。


完全了解
要完全理解我要解释的内容,必须将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 Page的{​​{1}}。 COLLATION就是对Windows Code Page的另一个映射。对于ENCODINGs Latin1_General_CI_AI,它返回COLLATION代码Windows Code Page,该代码映射到1252 Windows-1252
因此,对于带有ENCODING varchar的{​​{1}}列,此字段将使用Latin1_General_CI_AI COLLATION处理其数据,并且仅正确存储此字段支持的字符编码。

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

A text containing UNICODE characters, wrongfully being stored, due our collation and encoding on the varchar field

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

ENCODING / Windows-1252 ENCODING

在SQL Server 2019之前,我们只有NCHARNVARCHAR字段。有人说它们是NCHAR字段。 那是错误的!。同样,它取决于字段的NVARCHAR以及SQLServer版本。 微软的"nchar and nvarchar (Transact-SQL)" documentation完美地指定了:

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

换句话说,如果我们使用早于2012年的SQL Server,例如SQL Server 2008 R2,则这些字段的UNICODE将使用COLLATION,它支持ENCODING的子集。但是,如果我们使用SQL Server 2012或更高版本,并定义一个启用了UCS-2 ENCODING的{​​{1}},则在我们的字段中将使用UNICODE COLLATION,它完全支持{{ 1}}。


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

Supplementary Character / UTF-16

从SQL Server 2019开始,我们可以使用ENCODING / UNICODE 字段,并且仍使用CHAR {{1}来完全支持VARCHAR } !!!

来自微软的"char and varchar (Transact-SQL)" documentation

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

同样,换句话说,如果我们使用早于2019年的SQL Server,例如SQL Server 2008 R2,则需要使用前面说明的方法检查CHAR。但是,如果我们使用SQL Server 2019或更高版本,并定义一个VARCHAR之类的UNICODE,则我们的字段将使用UTF-8 ENCODING,这是迄今为止使用最广泛,效率最高的编码支持所有ENCODING个字符。


奖励信息:

关于OP对的观察,“我发现大多数欧洲语言(德语,意大利语,英语,...)在VARCHAR列的同一数据库中都可以使用” ,我认为这是很高兴知道为什么会这样:

对于最常见的COLLATION,例如默认值Latin1_General_100_CI_AS_SC_UTF8UTF-8ENCODING对于UNICODE字段将是COLLATIONs。如果我们看看它的documentation,我们可以看到它支持:

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

但是正如我之前说的,这与语言无关,与表情符号示例中所示的期望/支持的字符有关,或诸如“锂电池的电阻为0.5Ω”之类的句子,其中我们再次使用了简单的英语和希腊字母/字符“ omega”(以欧姆为单位的电阻符号),Latin1_General_CI_AI SQL_Latin1_General_CP1_CI_AS无法正确处理。

结论:

就这样!使用ENCODING / Windows-1252varchar / Windows-1252的时间取决于您要支持的字符,以及将决定哪个{{1 }},因此还有ENCODING可用。




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

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

  • varchar -是字符和字节序列之间的映射。因此,使用编码将字符“转换”为字节,反之亦然,从字节转换为字符。其中最受欢迎的是nvarcharCOLLATIONsENCODINGsUNICODE。您可以将其视为“转换表”(在此我进行了简化)。

  • code point -这个很重要。甚至Microsoft的文档也没有像应该这样澄清。排序规则指定了如何对数据进行排序,比较,和存储!。是的,我敢打赌您没想到最后一个,对吧! ENCODING上的排序规则也确定在特定UTF-8 / ISO-8859-1 / Windows-1252 / ASCII字段上使用的COLLATION

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

  • varchar -这是另一个nvarchar,是ASCII ENCODING中使用次数最多(如果不是最多的话)之一。它使用可变的字节宽度(根据规范,一个字符的长度可以从1到6个字节),并且完全支持所有UNICODE字符。

  • UNICODE -也是最常用的ENCODINGs之一,它在SQL Server上被广泛使用。它是固定大小的,因此每个字符始终为1个字节。它还支持多种语言的重音,但是不支持所有现有的重音,也不支持UTF-8 ENCODING这就是为什么您的ENCODING字段具有常见的排序规则,例如ENCODING支持UNICODEWindows-1252 ENCODINGENCODING字符,即使它没有使用支持UNICODE varchar

资源:
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/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-collation-name-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/en-us/sql/t-sql/statements/sql-server-collation-name-transact-sql?view=sql-server-ver15
https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver15#SQL-collations

SQL Server default character encoding
https://en.wikipedia.org/wiki/Windows_code_page

答案 3 :(得分:3)

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

答案 4 :(得分:2)

乔什说: “....当您使用Unicode时要记住一些事情虽然您可以在一个列中存储不同的语言,但您只能使用单个排序规则进行排序。有些语言使用拉丁字符但不像其他拉丁语言那样排序口音就是一个很好的例子,我不能记住这个例子,但是有一种东欧语言,其Y不像英语Y那样。然后有西班牙语用户在西班牙语用户出来后排序。 “

我是西班牙语母语,“ch”不是一个字母,而是两个“c”和“h”,西班牙字母表如下: abcdefghijklmn - opqrstuvwxyz 我们不期望“h”之后的“ch”而是“i” 除了ñ或HTML“&amp; ntilde;”

外,字母与英文相同

亚历

答案 5 :(得分:0)

<强> TL; DR;
Unicode - (nchar,nvarchar和ntext)
非unicode - (char,varchar和text)。

From 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)。

否则你的排序会很奇怪。

答案 6 :(得分:0)

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