varchar和nvarchar SQL Server数据types之间的主要性能差异是什么?

我正在使用SQL Server 2005在我学校的一个小型Web应用程序的数据库上工作。
我在varcharnvarchar的问题上看到了几个学派:

  1. 除非处理大量国际化数据,否则使用varchar ,然后使用nvarchar
  2. 只要使用nvarchar的一切。

我开始看到视图2的优点了。我知道nvarchar占用了两倍的空间,但这不一定是一笔巨大的交易,因为这只能为数百名学生存储数据。 对我来说,似乎是最简单的,不要担心它,只是允许一切使用nvarchar。 还是有什么我失踪?

始终使用nvarchar。

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

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

磁盘空间不是问题…但内存和性能将是。 双页读取,双倍索引大小,奇怪的LIKE和=常量行为等

你需要存储中文等脚本? 是或否…

而从MS BOL的“ 存储和Unicode的性能影响 ”

编辑

最近这个问题突出nvarchar性能可以是多么糟糕…

在nvarcharstring内search时,SQL Server使用高CPU

始终如一! 将NVARCHARjoinNVARCHAR会有很大的性能提升。

nvarchar在内存,存储,工作集和索引方面都会有很大的开销,所以如果规范说明它永远不会有必要的话,请不要麻烦。

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

另一方面,有很多列的情况下,我会提前问这个问题,如果我没有立即得到一个硬性和快速的答案,我会使列nvarchar。

对于你的应用程序,nvarchar很好,因为数据库的大小很小。 说“总是使用nvarchar”是一个巨大的过度简化。 如果你不需要存储汉字或其他疯狂的字符,使用VARCHAR,它将使用更less的空间。 我目前工作的前任在不需要的时候使用NVARCHARdevise了一些东西。 我们最近把它切换到了VARCHAR,并在该表上保存了15 GB(这是高度写入的)。 此外,如果您在该表上有一个索引,并且您想要包含该列或创build一个组合索引,则只会使索引文件的大​​小变大。

只要你的决定周到, 在SQL开发和数据定义中,似乎很less有“默认答案”(当然,不惜一切代价避免游标)。

由于您的应用程序很小,因此使用nvarchar over varchar基本上没有明显的成本增加,并且如果您需要存储unicode数据,则可以节省自己的麻烦。

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

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

无论是使用存储过程还是dynamic构buildSQL,都要确保所有string常量都以N为前缀(例如SET @foo = N'Hello world。'),所以常量也是Unicode。 这避免了在运行时的任何stringtypes转换。

因人而异。

一般来说; 从具有最less限制的最昂贵的数据types开始。 把它投入生产 。 如果性能开始成为问题,请查找这些nvarchar列中实际存储的内容。 那里有不适合varchar任何字符吗? 如果不是,切换到varchar。 在你知道疼痛的位置之前,不要尝试预先优化。 我的猜测是nvarchar / varchar之间的select不是什么会在可预见的将来减慢你的应用程序 。 应用程序中还有其他部分,性能调优会给你带来更多的钱

我可以从这方面的经验,谨防nvarchar 。 除非你绝对需要这个数据字段types,否则会破坏大型数据库的性能。 我inheritance了一个在性能和空间方面受到伤害的数据库。 我们可以将30GB的数据库缩小70%! 还有一些其他的修改,以帮助性能,但我敢肯定, varchar的帮助也显着。 如果您的数据库有可能增加表的数百万+logging不惜一切代价远离nvarchar

因为已经有不less,所以我还犹豫再加上一个答案,但是还有几点需要提出,要么是没有做出来,要么是没有明确的说出来。

第一:不要总是使用NVARCHAR 。 这是一个非常危险的,而且往往代价昂贵的态度/方法。 不要使用游标,因为它们有时是解决特定问题的最有效的手段,而做WHILE循环的通用解决方法几乎总是比正确完成的Cursor慢。

唯一的时候你应该使用“总是”这个词,就是build议“总是做最适合的情况”。 当然,这通常很难确定,尤其是当试图平衡开发时间中的短期收益(经理:“我们需要这个function – 直到现在才知道 – 一周前!”) (经理谁最初压力团队完成为期3个星期的冲刺3个月的项目:“为什么我们有这些性能问题?我们怎么可能做X没有灵活性?我们负担不起我们可以在一周内完成什么工作,这样我们就可以回到我们的优先项目中,而且我们肯定需要在devise上花费更多的时间,所以这种情况不会发生!“)。

第二: @ gbn的答案涉及到一些非常重要的要点,当做出某些数据build模决定时,path并不是100%清晰的。 但是还有更多需要考虑的事情:

  • 事务日志文件的大小
  • 花费时间来复制(如果使用复制)
  • ETL需要花费的时间(如果ETLing)
  • 将日志传送到远程系统并恢复所需的时间(如果使用日志传送)
  • 备份的大小
  • 完成备份所需的时间
  • 执行还原所需的时间(有时候这可能很重要;-)
  • tempdb所需的大小
  • 触发器的性能(对于存储在tempdb中的插入和删除的表)
  • 行版本的性能(如果使用SNAPSHOT ISOLATION,由于版本存储位于tempdb中)
  • 首席财务官说,他们去年在SAN上花费了100万美元,因此他们不会另外授权25万美元的额外存储空间
  • 执行INSERT和UPDATE操作所需的时间长度
  • 索引维护所需的时间
  • 等等等等。

浪费空间对整个系统有巨大的级联效应。 我写了一篇关于这个主题的文章: Disk Is Cheap! ORLY? (需要免费注册;对不起,我不能控制这个政策)。

第三:虽然有些答案不正确地关注“这是一个小应用”方面,有些答案正确地build议“使用适当的东西”,但没有任何答案为OP提供了真正的指导。问题中提到的一个重要细节是这是他们学校的网页。 大! 所以我们可以build议:

  • 学生和/或学院名称的字段可能应该是NVARCHAR因为随着时间的推移,只有来自其他文化的名字才会出现在这些地方。
  • 但是对于街道地址和城市名称? 该应用程序的目的没有说明(这将是有益的),但假设地址logging(如果有的话)仅适用于特定的地理区域(即单一的语言/文化),然后使用VARCHAR与适当的代码页这由字段的整理确定)。
  • 如果存储状态和/或国家的ISO代码(由于ISO代码是固定长度,人类可读,以及标准:),所以不需要存储INT / TINYINT CHAR(2)对于两个字母代码使用CHAR(2) ,如果使用3字母代码。
  • 如果存储邮政编码(即邮政编码),使用VARCHAR因为它是一个国际标准,永远不会使用AZ以外的任何字母。 是的,即使只存储美国邮政编码而不是INT,由于邮政编码不是数字,所以仍然使用VARCHAR ,它们是string,其中一些具有前导“0”。
  • 如果存储电子邮件地址和/或URL,请使用NVARCHAR因为这两者现在都可以包含Unicode字符。
  • 等等….

第四:现在你的NVARCHAR数据占据了比VARCHARNVARCHAR数据所需要的空间的两倍(“恰好适合”=不会变成“?”),并且以某种方式,就像魔术一样,应用程序确实增长了,现在至less有一个这样的字段中有数百万条logging,其中大多数行是标准的ASCII字段,但有些字段包含Unicode字符,所以您必须保留NVARCHAR ,请考虑以下几点:

  1. 如果您使用的是SQL Server 2008或更新版本,并且位于Enterprise Edition上,则可以启用数据压缩 。 数据压缩可以(但不会“总是”)压缩NCHARNVARCHAR字段中的Unicode数据。 决定因素是:

    1. NCHAR(1 - 4000)NVARCHAR(1 - 4000)使用Unicode的标准压缩scheme ,但仅在SQL Server 2008 R2中启动,并且仅适用于IN ROW数据,而不是OVERFLOW! 这似乎比常规的ROW / PAGE压缩algorithm更好。
    2. NVARCHAR(MAX)XML (我猜也是VARBINARY(MAX)TEXTNTEXT )IN ROW数据(LOB或OVERFLOW页面中的行不closures)可以至less是PAGE压缩,也可能是行压缩肯定这最后一个)。
    3. 任何OFF ROW数据,LOB或OVERLOW =没有为您压缩!
  2. 如果在Enterprise Edition上使用比2008版本旧的版本,则可以有两个字段:一个VARCHAR和一个NVARCHAR 。 例如,假设您正在存储大多数都是基本ASCII字符(值为0 – 127)的URL,因此适合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]计算列中进行select。 对于插入和更新,通过查看转换是否改变传入值(必须是NVARCHARtypes)来确定要使用哪个字段:

     INSERT INTO TableName (..., URLa, URLu) VALUES (..., IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL), IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL) ); 

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

  • 库存和定价的FTP源 – 当varchar正常工作时,项目描述和其他文本在nvarchar中。 将这些转换为varchar几乎减less了一半的文件大小,真正帮助上传。

  • 上面的场景工作正常,直到有人在项目描述中放置了一个特殊字符(可能是商标,不记得)

我仍然不使用nvarchar每次通过varchar。 如果有任何疑问或潜在的特殊字符,我使用nvarchar。 我发现我主要使用varchar当我在100%控制填充字段。

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

如果你想存储unicode, 希望兼容较旧的纯ASCII应用程序,我会认为使用VARCHAR()和UTF-8将是神奇的子弹:它只需要更多的空间。

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

当你想故意限制数据types以确保它包含来自某个特定集合的字符时,会有例外情况。 例如,我有一个场景,我需要将域名存储在数据库中。 域名国际化在当时是不可靠的,所以最好限制基础层面的投入,有助于避免任何潜在的问题。

如果仅仅因为系统存储过程需要它而使用NVARCHAR ,最常见的情况是使用sp_executesql ,而且dynamicSQL很长,那么从性能的angular度来看,更好的做所有的string操作(连接,replace等) VARCHAR然后将最终结果转换为NVARCHAR并将其提供给proc参数。 所以不,不要总是使用NVARCHAR