ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

我正在寻找类似的东西:

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

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

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

两个最受好评的问题都是错误。它与“存储不同/多种语言”无关。您可以使用常见的 varchar 来支持 ñ 和英语等西班牙语字符。
我刚刚发布了一个答案,解释并提供了有关如何处理它的更新方法。

C
Cade Roux

您想要使用 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 否则


需要注意的是,“不同的语言” 不仅仅意味着不同的行可以包含来自不同语言的值。这也意味着如果数据库的默认排序规则(即服务器机器的区域设置)与任何客户端计算机的区域设置不同。例如,服务器机器设置为 en-US,但我的 PC 设置为 fr-US
@IanBoyd 一般来说,当在一个列中混合语言并在一个集合中返回多种语言的项目并使用该排序规则进行排序时,排序规则将是一个很大的问题。排序规则也会对被组合为一个的字符产生影响(匈牙利语 dz 和 ly):sqlservercentral.com/Forums/Topic19439-9-1.aspx stackoverflow.com/questions/7207590/… - nvarchar 无法解决该问题
许多亚洲国家(包括中国)在其车牌中使用标志,因此除非您 100% 确定您的程序绝对不会也永远不会处理此类数据,否则最好使用 nvarchar 作为车牌.是的,这包括交通违规者登记、停车场和车辆运输方法等内容。来自中国的人完全有可能乘渡轮甚至开车到您的国家并将汽车停在您的车库中。
这是不正确的: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-1252 编码,因此您将无法存储表情符号、希腊字母 Ω 字符(例如)。该字节序列的 Windows-1252 编码中没有正确的映射。 将存储为 ?,而 Ω 将转换为 O
V
Vitox

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

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

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

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

MSSQL Server 使用 COLLATION 来确定在 char/nchar/varchar/nvarchar 字段上使用什么 ENCODING。因此,与很多人想的不同,COLLATION不仅仅是对数据进行排序和比较,还涉及 ENCODING,因此:我们的数据将如何存储!强>

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

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

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

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

https://i.stack.imgur.com/5FHHF.png

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

NCHAR/NVARCHAR

在 SQL Server 2019 之前,我们只有 NCHARNVARCHAR 字段。有人说它们是 UNICODE 字段。 这是错误的!。同样,它取决于字段的 COLLATION 以及 SQLServer 版本。 Microsoft 的 "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,则这些字段的 ENCODING 将使用支持 UNICODE 子集的 UCS-2 ENCODING。但是,如果我们使用 SQL Server 2012 或更新版本,并定义一个启用了 Supplementary CharacterCOLLATION,那么我们的字段将使用完全支持 UNICODEUTF-16 ENCODING

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

CHAR/VARCHAR

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

来自 Microsoft 的 "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,我们需要使用前面解释的方法检查 ENCODING。但是,如果我们使用 SQL Server 2019 或更新版本,并定义像 Latin1_General_100_CI_AS_SC_UTF8 这样的 COLLATION,那么我们的字段将使用 UTF-8 ENCODING,这是迄今为止支持所有 UNICODE 字符的最常用和最有效的编码.

奖金信息:

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

对于最常见的 COLLATIONs,例如默认的 Latin1_General_CI_AISQL_Latin1_General_CP1_CI_AS,对于 varchar 字段,ENCODING 将是 Windows-1252。如果我们看一下它的 documentation,我们可以看到它支持:

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

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

结论:

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

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

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

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

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

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

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

Windows-1252 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/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


J
JoshBerke

每当您必须存储多种语言时,都应该使用 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 字符更容易,避免额外的转换并占用空间。因此,我之前的声明。


>>您应该在必须存储多种语言的任何时候使用 NVARCHAR 这不是真的。德语、意大利语和英语非常适合与 VARCHAR 列在同一张表中。请更具体
有关匈牙利语中 dz 和 ly 的示例,请参见 sqlservercentral.com/Forums/Topic19439-9-1.aspxstackoverflow.com/questions/7207590/…
c
cherouvim

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


A
Alex

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

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

亚历克斯


嗨,亚历克斯,你曾经在 1 列中存储过不同的语言吗?我们在一张表中有不同语言的不同列。
他们可能指的是捷克语。我们在“h”和“i”之间有“ch”,它是一个单独的字母。
M
Matas Vaitkevicius

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

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


“Unicode -(nchar、nvarchar 和 ntext)。非 unicode -(char、varchar 和 text)。”这是不正确的。 nchar/nvarchar 在不同版本的 SQL Server 中使用不同的 encodings,例如不完全支持 UNICODEUCS-2
U
Umar Farooq

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