我正在学校使用一个小型网络应用程序开发数据库 SQL Server 2005.
我看到关于这个问题的几种思想流派 varcharnvarchar:

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

我开始看到观点 2 的优点。我知道 nvarchar 确实占用了两倍的空间,但这不一定是一个大问题,因为它只会存储几百个学生的数据。对我来说,最简单的方法就是不用担心这个问题,只允许所有内容都使用 nvarchar。或者我缺少什么?

有帮助吗?

解决方案

始终使用 nvarchar。

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

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

其他提示

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

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

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

编辑:

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

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

始终如一!将 VARCHAR 联接到 NVARCHAR 对性能有很大影响。

nvarchar 将在内存、存储、工作集和索引方面产生大量开销,因此如果规范规定它确实会 绝不 有必要,不用打扰。

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

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

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

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

我犹豫是否要在这里添加另一个答案,因为已经有很多答案了,但需要指出一些尚未提出或未明确提出的问题。

第一的:不是 总是使用 NVARCHAR. 。这是一种非常危险且往往代价高昂的态度/方法。更不用说“绝不 使用游标”,因为它们有时是解决特定问题的最有效手段,并且是执行以下操作的常见解决方法 WHILE 循环几乎总是比循环慢 适当地 完成光标。

唯一应该使用“总是”这个词的时候是建议“总是做对情况最有利的事情”。当然,这通常很难确定,特别是在尝试平衡开发时间的短期收益时(经理:“我们需要这个功能——直到现在——一周前你才知道!”)以及长期维护成本(经理最初向团队施压,要求他们在 3 周的冲刺中完成 3 个月的项目:“为什么我们会遇到这些性能问题?没有灵活性,我们怎么可能做X呢?我们无法承受一两次冲刺来解决这个问题。我们可以在一周内完成哪些工作,以便回到我们的优先事项?我们肯定需要在设计上花更多的时间,这样这种情况就不会再发生了!”)。

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

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

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

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

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

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

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

    1. NCHAR(1 - 4000)NVARCHAR(1 - 4000) 使用 Unicode 标准压缩方案, ,但仅从 SQL Server 2008 R2 开始,并且仅适用于 IN ROW 数据,不适用于 OVERFLOW!这似乎比常规的 ROW/PAGE 压缩算法更好。
    2. NVARCHAR(MAX)XML (而且我想也 VARBINARY(MAX), TEXT, , 和 NTEXT)IN ROW(不在 LOB 或 OVERFLOW 页中的行外)的数据至少可以进行 PAGE 压缩,但是 不是 ROW 压缩。当然,PAGE 压缩取决于行内值的大小:我使用 VARCHAR(MAX) 进行测试,发现 6000 个字符/字节行不会压缩,但 4000 个字符/字节行会压缩。
    3. 任何 OFF ROW 数据、LOB 或 OVERLOW = 不进行压缩!
  2. 如果使用 SQL Server 2005 或 2008 - 2016 RTM 和 不是 在企业版上,您可以有两个字段:一 VARCHAR 和一个 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 库)附带 实用程序_GZip实用程序_GUnzip 在免费版本中
    • 对于 SQL Server 2016 及更高版本:你可以使用内置的 COMPRESSDECOMPRESS 函数,也是 GZip。
  4. 如果使用 SQL Server 2017 或更高版本,您可以考虑将表设为聚集列存储索引。

  5. 虽然这还不是一个可行的选择,但 SQL Server 2019 在以下版本中引入了对 UTF-8 的本机支持: VARCHAR / CHAR 数据类型。目前它有太多的错误,无法使用,但如果它们被修复,那么这是一个选项 一些 场景。请看我的帖子“SQL Server 2019 中的本机 UTF-8 支持:救世主还是假先知?”,详细分析这一新功能。

由于您的应用程序很小,因此使用 nvarchar 相对于 varchar 基本上没有明显的成本增加,并且如果您需要存储 unicode 数据,您可以避免将来可能遇到的麻烦。

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

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

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

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

YMMV。

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

我在工作中经常遇到这样的问题:

  • 库存和定价的 FTP 源 - 当 varchar 工作正常时,商品描述和其他文本使用 nvarchar。将它们转换为 varchar 可以将文件大小几乎减少一半,并且确实有助于上传。

  • 上面的场景工作得很好,直到有人在物品描述中添加了特殊字符(也许是商标,不记得了)

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

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

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

对于那些不熟悉 UTF-8 的人,我可以推荐一下吗 底漆.

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top