总是使用nvarchar(MAX)有什么缺点吗?

stu*_*ell 329 sql sql-server sql-server-2005

在SQL Server 2005中,使所有字符字段nvarchar(MAX)而不是明确指定长度是否有任何缺点,例如nvarchar(255)?(除了显而易见的一个,你无法限制数据库级别的字段长度)

Dav*_*eps 146

在MSDN论坛上询问了同样的问题:

从原始帖子(更多信息):

将数据存储到VARCHAR(N)列时,值以相同的方式物理存储.但是当您将其存储到VARCHAR(MAX)列时,屏幕后面的数据将作为TEXT值处理.因此在处理VARCHAR(MAX)值时需要一些额外的处理.(仅当尺寸超过8000时)

VARCHAR(MAX)或NVARCHAR(MAX)被视为"大值类型".大值类型通常存储在"行外".这意味着数据行将有一个指向存储"大值"的另一个位置的指针...

  • 我把答案读作"不,使用`N/VARCHAR(MAX)`"没有任何缺点,因为只有当尺寸超过8000时才会有额外的处理.因此,只有在必要时**才会产生成本**,而且您的数据库**限制性较小**.我读错了吗?好像你几乎总是想要`N/VARCHAR(MAX)`而不是`N/VARCHAR(1-8000)`... (71认同)
  • 不幸的是,这个答案有很多问题.它使8k边界看起来像一个神奇的数字,不是真的,根据可能更多的因素,包括`sp_tableoptions`,值被推出行:http://msdn.microsoft.com/en-us/library/ms173530的.aspx.VARCHAR(255)类型也可以被推出行,提到的'开销'可能与MAX和255完全相同.它将MAX类型与TEXT类型进行比较,当它们是不同的时候(完全不同的API来操纵,不同的存储等).它没有提到实际的差异:没有索引,没有MAX类型的在线操作 (62认同)
  • 如果我没记错的话,如果尺寸超过8k,它们是否只存储在行外? (16认同)
  • 那么问题是,使用N/VARCHAR(MAX)和N/TEXT之间有区别吗? (2认同)

ale*_*mac 47

这是一个公平的问题,除了明显的......

缺点可能包括:

性能影响查询优化器使用字段大小来确定最有效的exectution计划

"1.数据库的扩展和页面中的空间分配是灵活的.因此,当使用更新向字段添加信息时,如果新数据比先前插入的数据长,则数据库必须创建指针.这个数据库文件将变得支离破碎=从索引到删除,更新和插入几乎所有内容都会降低性能." http://sqlblogcasts.com/blogs/simons/archive/2006/02/28/Why-use-anything-but-varchar_2800_max_2900_.aspx

集成的影响 - 其他系统很难知道如何与数据库集成不可预测的数据增长可能的安全问题,例如,您可能通过占用所有磁盘空间来使系统崩溃

这里有好文章:http: //searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1098157,00.html

  • +1对于集成和安全性的影响.当大多数其他答案谈论性能时,这些是一个原始角度.与集成含义相关的任何使用元数据提供合理的默认控件大小的工具(如报表编写者或表单设计者)如果所有列都是`varchar(max)`则需要更多的工作. (4认同)

Bil*_*win 28

有时您希望数据类型对其中的数据强制执行某些意义.

比如说你有一个真的不应该长于20个字符的列.如果您将该列定义为VARCHAR(MAX),则某些恶意应用程序可能会在其中插入一个长字符串,您将永远不知道,或者有任何方法可以阻止它.

下次您的应用程序使用该字符串时,假设字符串的长度对于它所代表的域来说是适度且合理的,您将会遇到不可预测且令人困惑的结果.

  • 对作者没用.他明确地排除了你给出答案的这个问题. (10认同)
  • 我同意这一点,以及其他一些评论,但我仍然认为这是业务层的责任.当它到达数据库层时,无论多么荒谬,它都应该快速致敬并存储该值.我认为这里真正起作用的是,我认为90%的时候开发人员指定varchar(255),他的意图不是真正的255个字符,而是一些未指定的中等长度值.考虑到我的数组中不合理的大值与不可预见的异常之间的权衡,我将采用较大的值. (7认同)
  • @ Chris B Behrens:我不同意; 数据库模式_is_是业务逻辑的一部分.表,关系,字段和数据类型的选择都是业务逻辑 - 值得使用RDBMS来强制执行此业务逻辑的规则.出于一个原因,很少有一个应用层访问数据库; 例如,您可能拥有绕过主业务层的数据导入和提取工具,这意味着您确实需要数据库来强制执行规则. (6认同)
  • 如果他们指定VARCHAR(255)来指示一些未知的长度,那么他们的错误就是没有正确地研究他们正在设计的内容.解决方案是让开发人员完成他们的工作,而不是让数据库允许不合理的值. (4认同)

Tim*_*ell 26

根据接受的答案中提供的链接,似乎:

  1. 存储在nvarchar(MAX)字段中的100个字符将在字段中存储与100个字符不同nvarchar(100)- 数据将以内联方式存储,您不会有"读取和写入数据"的开销.所以不用担心.

  2. 如果大小大于4000,数据将自动存储在"行外",这就是您想要的.所以也不用担心.

然而...

  1. 您无法在nvarchar(MAX)列上创建索引.您可以使用全文索引,但无法在列上创建索引以提高查询性能.对我来说,这封印了这笔交易......总是使用nvarchar(MAX)是一个明显的缺点.

结论:

如果你想在整个数据库中使用一种"通用字符串长度",它可以被编入索引并且不会浪费空间和访问时间,那么你可以使用nvarchar(4000).

  • 谢谢,对我来说这是最终的答案。我问自己同样的问题 - *为什么不一直使用 `nvarchar(max)` - 就像 C# 中的 `string`?* - 但第 3 点(索引问题)给出了答案。 (2认同)

QMa*_*ter 19

我检查了一些文章并从中找到了有用的测试脚本:http://www.sqlservercentral.com/Forums/Topic1480639-1292-1.aspx 然后将其更改为NVARCHAR(10)与NVARCHAR(4000)与NVARCHAR(MAX)之间的比较)当使用指定的数字但使用MAX时,我找不到速度差异.你可以自己测试一下.希望这个帮助.

SET NOCOUNT ON;

--===== Test Variable Assignment 1,000,000 times using NVARCHAR(10)
DECLARE @SomeString NVARCHAR(10),
        @StartTime DATETIME;
--=====         
 SELECT @startTime = GETDATE();
 SELECT TOP 1000000
        @SomeString = 'ABC'
   FROM master.sys.all_columns ac1,
        master.sys.all_columns ac2;
 SELECT testTime='10', Duration = DATEDIFF(ms,@StartTime,GETDATE());
GO
--===== Test Variable Assignment 1,000,000 times using NVARCHAR(4000)
DECLARE @SomeString NVARCHAR(4000),
        @StartTime DATETIME;
 SELECT @startTime = GETDATE();
 SELECT TOP 1000000
        @SomeString = 'ABC'
   FROM master.sys.all_columns ac1,
        master.sys.all_columns ac2;
 SELECT testTime='4000', Duration = DATEDIFF(ms,@StartTime,GETDATE());
GO
--===== Test Variable Assignment 1,000,000 times using NVARCHAR(MAX)
DECLARE @SomeString NVARCHAR(MAX),
        @StartTime DATETIME;
 SELECT @startTime = GETDATE();
 SELECT TOP 1000000
        @SomeString = 'ABC'
   FROM master.sys.all_columns ac1,
        master.sys.all_columns ac2;
 SELECT testTime='MAX', Duration = DATEDIFF(ms,@StartTime,GETDATE());
GO
Run Code Online (Sandbox Code Playgroud)

  • 那很有意思.在我的盒子上,MAX似乎慢了4倍. (3认同)
  • SQL Server 2012上的新结果:10比4k慢两倍,MAX比4k慢5.5倍. (3认同)
  • SSD上的SQL Server 2014:150,156,716(10,4000,MAX). (3认同)
  • 感谢您为此讨论添加一些实数.我们经常忘记构建测试用例是最快捷的洞察方式. (2认同)

Ale*_*lex 13

把它想象成另一个安全级别.您可以设计没有外键关系的表 - 完全有效 - 并确保完全在业务层上存在关联实体.但是,外键被认为是很好的设计实践,因为它们会增加另一个约束级别,以防万一在业务层上出现问题.字段大小限制也是如此,不使用varchar MAX.


Nic*_*ias 8

不使用max或text字段的原因是你无法执行在线索引重建,即使用SQL Server企业版,REBUILD WITH ONLINE = ON.


use*_*740 5

自 SQL Server 2019 起,即使使用行内数据存储进行存储,NVARCHAR(MAX)仍然不支持 SCSU \xe2\x80\x9cUnicode 压缩\xe2\x80\x9d \xe2\x80\x94。SCSU 是在 SQL Server 2008 中添加的,适用于任何 ROW/PAGE 压缩表和索引。

\n

因此,即使存储在 LOB 中,NVARCHAR(MAX) 也可能占用具有相同文本内容+ \xe2\x80\x94 的 NVARCHAR(1..4000) 字段两倍的物理磁盘空间。非 SCSU 浪费取决于所代表的数据和语言。

\n

Unicode 压缩实现

\n
\n

SQL Server 使用 Unicode 标准压缩方案 (SCSU) 算法的实现来压缩存储在行或页压缩对象中的 Unicode 值。对于这些压缩对象,对于 nchar(n) 和 nvarchar(n) 列,Unicode 压缩是自动的[并且从不与 nvarchar(max) 一起使用]。

\n
\n

另一方面,如果将 NVARCHAR(MAX) 列写入行内数据,则 PAGE 压缩(自 2014 年起)仍然适用于它们。因此缺乏 SCSU 感觉就像 \xe2\x80\x9cmissing optimization\xe2\x80\x9d 。与 SCSU 不同,页面压缩结果可能因共享前导前缀(即重复值)而有很大差异。

\n

但是,即使使用 OPENJSON 等函数由于避免隐式转换而导致 IO 成本较高,但使用 NVARCHAR(MAX) 仍可能是 \xe2\x80\x9cfaster\xe2\x80\x9d 。这是隐式转换开销,取决于相对使用成本以及过滤之前或之后是否触及该字段。在 VARCHAR(MAX) 列中使用 2019\xe2\x80\x99s UTF-8 排序规则时,也存在同样的转换问题。

\n

使用 NVARCHAR(1-4000) 还需要约 8000 字节行配额中的 N*2 字节,而 NVARCHAR(MAX) 仅需要 24 字节。总体设计和使用需要一起考虑,以考虑具体的实现细节。

\n

+在我的数据库/数据/模式中,通过使用两列(读取时合并),可以将磁盘空间使用量减少约 40%,同时仍然支持溢出文本值。SCSU 虽然有缺陷,但却是一种非常聪明且未得到充分利用的方法,可以更节省空间地存储 Unicode。

\n