244

我正在使用SQL Server 2005. 我在vs
问题上看到了一些思想流派:varcharnvarchar

  1. varchar除非您处理大量国际化数据,否则请使用nvarchar.
  2. 只是nvarchar用于一切。

我开始看到观点 2 的优点。我知道 nvarchar 确实占用了两倍的空间,但这并不一定是一个大问题,因为它只会为几百名学生存储数据。对我来说,似乎最容易不用担心它,只允许一切使用 nvarchar。还是我缺少什么?

4

14 回答 14

232

磁盘空间不是问题……但内存和性能才是问题。翻倍的页面读取、双倍的索引大小、奇怪的 LIKE 和 = 恒定行为等

需要存储中文等脚本吗?是还是不是...

以及来自 MS BOL “ Unicode 的存储和性能影响

编辑

最近的 SO 问题强调了 nvarchar 性能有多糟糕......

SQL Server 在 nvarchar 字符串中搜索时使用高 CPU

于 2008-10-13T19:33:23.713 回答
137

始终使用 nvarchar。

大多数应用程序可能永远不需要双字节字符。但是,如果您需要支持双字节语言并且您的数据库模式中只有单字节支持,那么在整个应用程序中返回和修改非常昂贵。

将一个应用程序从 varchar 迁移到 nvarchar 的成本将远远超过您将在大多数应用程序中使用的一点点额外磁盘空间。

于 2008-08-29T21:44:41.600 回答
61

始终如一!将 VARCHAR 加入 NVARCHAR 对性能有很大影响。

于 2008-10-31T16:32:03.950 回答
47

nvarchar 将在内存、存储、工作集和索引方面产生大量开销,因此如果规范规定它真的永远没有必要,请不要打扰。

我不会有一个硬性和快速的“总是 nvarchar”规则,因为它在许多情况下可能是完全浪费 - 特别是来自 ASCII/EBCDIC 的 ETL 或标识符和代码列,它们通常是键和外键。

另一方面,有很多列的情况,我一定会尽早提出这个问题,如果我没有立即得到一个明确而快速的答案,我会将列设为 nvarchar。

于 2008-10-31T16:37:35.390 回答
40

我犹豫在这里添加另一个答案,因为已经有很多了,但是需要提出一些没有提出或没有明确提出的观点。

第一:不要总是使用NVARCHAR. 这是一种非常危险且通常代价高昂的态度/方法。说“从不使用游标”也没什么好说的,因为它们有时是解决特定问题的最有效方法,而WHILE循环的常见解决方法几乎总是比正确完成的游标慢。

您应该使用“始终”一词的唯一时间是在建议“始终做最适合情况的事情”时。当然,这通常很难确定,尤其是在尝试平衡开发时间的短期收益时(经理:“我们需要这个功能——直到现在你才知道——一周前!”) - 长期维护成本(最初要求团队在 3 周 sprint 中完成 3 个月项目的经理:“为什么我们会遇到这些性能问题?我们怎么可能完成没有灵活性的 X?我们负担不起一两个冲刺来解决这个问题。我们可以在一周内完成什么,以便我们可以回到我们的优先项目?我们肯定需要花更多的时间在设计上,这样就不会继续发生这种情况了!”)。

第二: @gbn 的回答涉及在路径不是 100% 清晰时做出某些数据建模决策时要考虑的一些非常重要的点。但还有更多需要考虑:

  • 事务日志文件的大小
  • 复制所需的时间(如果使用复制)
  • ETL 所需的时间(如果 ETLing)
  • 将日志传送到远程系统并恢复所需的时间(如果使用日志传送)
  • 备份大小
  • 完成备份所需的时间长度
  • 进行恢复所需的时间长度(这在某天可能很重要;-)
  • tempdb 所需的大小
  • 触发器的性能(对于存储在 tempdb 中的插入和删除表)
  • 行版本控制的性能(如果使用 SNAPSHOT ISOLATION,因为版本存储在 tempdb 中)
  • 当 CFO 说他们去年刚刚在 SAN 上花费了 100 万美元,因此他们不会再授权 25 万美元用于额外存储时,获得新磁盘空间的能力
  • 执行 INSERT 和 UPDATE 操作所需的时间长度
  • 进行索引维护所需的时间长度
  • 等等等等等等。

浪费空间会对整个系统产生巨大的级联效应。我写了一篇文章详细介绍了这个主题:磁盘很便宜!奥利?(需要免费注册;抱歉,我无法控制该政策)。

第三:虽然有些答案错误地集中在“这是一个小应用程序”方面,有些答案正确地建议“使用合适的”,但没有一个答案为 OP 提供了真正的指导问题中提到的一个重要细节这是他们学校的网页。伟大的!所以我们可以建议:

  • 学生和/或教师姓名的字段可能应该是NVARCHAR,因为随着时间的推移,来自其他文化的名字出现在这些地方的可能性只会越来越大。
  • 但是对于街道地址和城市名称?没有说明应用程序的目的(这会很有帮助),但假设地址记录(如果有)仅属于特定地理区域(即单一语言/文化),然后VARCHAR与适当的代码页(其中由字段的排序规则确定)。
  • 如果存储州和/或国家 ISO 代码(无需存储INT/ TINYINT,因为 ISO 代码是固定长度的、人类可读的,并且是标准的 :) 使用CHAR(2)两个字母代码,CHAR(3)如果使用 3 个字母代码。并考虑使用二进制排序规则,例如Latin1_General_100_BIN2.
  • 如果存储邮政编码(即邮政编码),请使用,VARCHAR因为国际标准是绝不使用 AZ 以外的任何字母。VARCHAR是的,即使只存储美国邮政编码而不是 INT ,仍然使用,因为邮政编码不是数字,它们是字符串,其中一些具有前导“0”。并考虑使用二进制排序规则,例如Latin1_General_100_BIN2.
  • 如果存储电子邮件地址和/或 URL,请使用它们,NVARCHAR因为它们现在都可以包含 Unicode 字符。
  • 等等....

第四:现在您的NVARCHAR数据占用的空间是非常适合的数据所需空间的两倍VARCHAR(“非常适合”=不会变成“?”)并且不知何故,就像魔术一样,应用程序确实增长了现在至少在其中一个字段中有数百万条记录,其中大多数行是标准 ASCII,但有些包含 Unicode 字符,因此您必须保留NVARCHAR,请考虑以下事项:

  1. 如果您使用 SQL Server 2008 - 2016 RTM并且在 Enterprise Edition 上,或者如果使用 SQL Server 2016 SP1(所有版本都提供数据压缩)或更高版本,那么您可以启用Data Compression。数据压缩可以(但不会“总是”)压缩NCHARNVARCHAR字段中的 Unicode 数据。决定因素是:

    1. NCHAR(1 - 4000)NVARCHAR(1 - 4000)使用Unicode 的标准压缩方案,但仅从 SQL Server 2008 R2 开始,并且仅用于 IN ROW 数据,而不是 OVERFLOW!这似乎比常规的 ROW / PAGE 压缩算法要好。
    2. NVARCHAR(MAX)XML(我猜还有VARBINARY(MAX),TEXTNTEXT)IN ROW 数据(不在 LOB 或 OVERFLOW 页面中的行外)至少可以 PAGE 压缩,但不能ROW 压缩。当然,PAGE 压缩取决于行内值的大小:我使用 VARCHAR(MAX) 进行了测试,发现 6000 个字符/字节的行不会压缩,但 4000 个字符/字节的行可以。
    3. 任何 OFF ROW 数据、LOB 或 OVERLOW = 无压缩!
  2. 如果使用 SQL Server 2005 或 2008 - 2016 RTM 而不是企业版,则可以有两个字段: oneVARCHAR和 one NVARCHAR。例如,假设您存储的 URL 大多都是基本 ASCII 字符(值 0 - 127),因此适合VARCHAR,但有时包含 Unicode 字符。您的架构可以包含以下 3 个字段:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    在此模型中,您只能[URL]计算列中选择。对于插入和更新,您可以通过查看转换是否更改传入值来确定要使用的字段,该值必须是以下NVARCHAR类型:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. 您可以 GZIP 传入的值VARBINARY(MAX),然后在输出时解压缩:

    • 对于 SQL Server 2005 - 2014:您可以使用 SQLCLR。SQL# (我编写的一个 SQLCLR 库)在免费版本中带有Util_GZipUtil_GUnzip
    • 对于 SQL Server 2016 及更高版本:您可以使用内置COMPRESSDECOMPRESS函数,它们也是 GZip。
  4. 如果使用 SQL Server 2017 或更高版本,您可以考虑将表设为聚集列存储索引。

  5. 虽然这还不是一个可行的选项,但 SQL Server 2019 在VARCHAR/CHAR数据类型中引入了对 UTF-8 的本机支持。目前它有太多的错误无法使用,但如果它们被修复,那么这是某些场景的一个选项。请参阅我的帖子“ SQL Server 2019 中的 Native UTF-8 Support: Savior or False Prophet? ”,详细分析这个新特性。

于 2015-09-30T16:41:48.147 回答
22

对于您的应用程序,nvarchar 很好,因为数据库很小。说“始终使用 nvarchar”过于简单化了。如果您不需要存储诸如汉字或其他疯狂字符之类的东西,请使用 VARCHAR,它将使用更少的空间。我目前工作的前任在不需要时使用 NVARCHAR 设计了一些东西。我们最近将它切换为 VARCHAR 并在该表上节省了 15 GB(它被高度写入)。此外,如果您在该表上有一个索引并且您想要包含该列或创建一个复合索引,那么您只是使您的索引文件大小更大。

请在您的决定中深思熟虑;在 SQL 开发和数据定义中,似乎很少有“默认答案”(当然,除了不惜一切代价避免使用游标)。

于 2009-01-30T00:46:20.503 回答
11

由于您的应用程序很小,因此使用 nvarchar 而不是 varchar 基本上没有明显的成本增加,并且如果您需要存储 unicode 数据,您可以省去潜在的麻烦。

于 2008-08-29T21:48:47.983 回答
8

一般来说; 从约束最少的最昂贵的数据类型开始。投入生产。如果性能开始成为问题,请找出这些nvarchar列中实际存储的内容。里面有没有不适合的角色varchar?如果没有,请切换到 varchar。在你知道痛苦在哪里之前,不要尝试预先优化。我的猜测是nvarchar/varchar 之间的选择不会在可预见的未来减慢您的应用程序的速度。在应用程序的其他部分中,性能调整会给您带来更多的收益

于 2013-02-22T07:42:39.623 回答
7

在过去的几年里,我们所有的项目都使用 NVARCHAR,因为所有这些项目都是多语言的。从外部源(例如 ASCII 文件等)导入的数据在插入数据库之前被上转换为 Unicode。

我还没有遇到较大索引等与性能相关的问题。索引确实使用更多内存,但内存很便宜。

无论您使用存储过程还是动态构建 SQL,请确保所有字符串常量都以 N 为前缀(例如 SET @foo = N'Hello world.';),因此该常量也是 Unicode。这避免了运行时的任何字符串类型转换。

YMMV。

于 2009-01-30T01:24:21.987 回答
7

我可以从经验中说出来,小心nvarchar。除非您绝对需要,否则此数据字段类型会破坏较大数据库的性能。我继承了一个在性能和空间方面受到伤害的数据库。我们能够将 30GB 的数据库大小减少 70%!还进行了一些其他修改以帮助提高性能,但我相信varchar's 对此也有很大帮助。nvarchar如果您的数据库有可能将表增加到一百万条以上的记录,则不惜一切代价 远离。

于 2011-04-04T12:26:51.753 回答
4

我在工作中经常处理这个问题:

  • 库存和定价的 FTP 提要 - 当 varchar 工作正常时,项目描述和其他文本在 nvarchar 中。将这些转换为 varchar 将文件大小几乎减少了一半,并且确实有助于上传。

  • 上面的场景运行良好,直到有人在项目描述中添加了一个特殊字符(可能是商标,不记得了)

我仍然不会每次都在 varchar 上使用 nvarchar。如果对特殊字符有任何疑问或可能,我使用 nvarchar。我发现当我 100% 控制填充该字段的内容时,我主要使用 varchar。

于 2008-12-05T17:20:47.250 回答
3

为什么在所有这些讨论中都没有提到 UTF-8?能够存储字符的完整 unicode 跨度并不意味着必须始终为每个字符分配两个字节(或“代码点”以使用 UNICODE 术语)。所有的 ASCII 都是 UTF-8。SQL Server 是否检查 VARCHAR() 字段的文本是否为严格的 ASCII(即最高字节位为零)?我希望不会。

如果那时您想存储 unicode希望与旧的仅 ASCII 应用程序兼容,我认为使用 VARCHAR() 和 UTF-8 将是灵丹妙药:它只在需要时使用更多空间。

对于那些不熟悉 UTF-8 的人,我可以推荐一个入门.

于 2009-12-10T00:10:00.697 回答
2

当您想要故意限制数据类型以确保它包含来自某个集合的字符时,会有一些例外情况。例如,我有一个场景需要将域名存储在数据库中。域名的国际化当时并不可靠,因此最好将输入限制在基础级别,并有助于避免任何潜在问题。

于 2015-09-04T04:10:49.077 回答
1

如果您NVARCHAR只是因为系统存储过程需要它而使用它,最常见的情况是莫名其妙sp_executesql的,并且您的动态 SQL 很长,那么从性能角度来看,您最好在VARCHAR转换时执行所有字符串操作(连接、替换等)最终结果NVARCHAR并将其输入 proc 参数。所以不,不要总是使用NVARCHAR

于 2017-04-12T18:17:18.213 回答