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

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

在MSDN论坛上也提出了同样的问题:

  • Varchar(max)vs Varchar(255)

从原来的post(更多的信息):

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

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

这是一个公平的问题,他除了显而易见的状态外,

缺点可能包括:

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

“1.扩展中的空间分配和数据库的页面是灵活的,因此当使用update添加信息到字段时,如果新数据比以前插入的数据长,那么你的数据库必须创build一个指针。成为碎片=几乎所有的东西,从索引删除,更新和插入性能较低。“ 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

有时你希望数据types对数据有一定的意义。

举例来说,你有一个不应超过20个字符的列。 如果将该列定义为VARCHAR(MAX),则某些stream氓应用程序可能会插入一个长string,而您永远不会知道,或者有任何方法阻止它。

下一次您的应用程序使用该string时,假设string的长度对于其所代表的域是适度和合理的,则会遇到不可预知且令人困惑的结果。

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

 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 

把它看作是另一个安全级别。 您可以devise您的表,而无需外键关系 – 完全有效 – 并确保完全在业务层上存在关联的实体。 但是,外键被认为是很好的devise实践,因为它们会添加另一个约束级别,以防业务层上出现混乱。 字段大小限制也是一样,不要使用varchar MAX。

根据接受的答案中提供的链接,看起来如下:

  1. 存储在nvarchar(MAX)字段中的100个字符在nvarchar(100)字段中的存储方式不会超过100个字符 – 数据将以内联方式存储,并且不会产生读写数据的“行外”开销。 所以不用担心

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

然而…

  1. 您不能在nvarchar(MAX)列上创build索引。 您可以使用全文索引,但不能在列上创build索引以提高查询性能。 对我来说,这个交易是密不可分的…总是使用nvarchar(MAX)是一个明显的缺点。

结论:

如果你想在整个数据库中使用一种“通用string长度”,可以索引,并且不会浪费空间和访问时间,那么你可以使用nvarchar(4000)

不使用max或text字段的原因是,即使使用SQL Server企业版,也不能执行联机索引重build,即REBUILD WITH ONLINE = ON。

我发现的唯一问题是我们在SQL Server 2005上开发我们的应用程序,并且在一个实例中,我们必须支持SQL Server 2000.我刚刚学会了SQL Server 2000不喜欢varchar的MAX选项或为nvarchar。

当你知道这个字段的时候,会出现一个错误的想法,例如5到10个字符。 我想我只会用max,如果我不确定长度是多less。 例如,电话号码永远不会超过一定数量的字符。

你能诚实地说,你是不确定的表中的每个领域的大致的长度要求?

我确实明白你的观点 – 有一些我肯定会考虑使用varchar(max)的领域。

有趣的是MSDN文档总结得非常好:

当列数据条目的大小相差很大时,使用varchar。 当列数据条目的大小相差很大时,使用varchar(max),大小可能会超过8,000字节。

这里有一个有趣的讨论 。

数据库的工作是存储数据,以便企业可以使用。 使数据有用的部分是确保它有意义。 允许某人为其名字input不限数量的字符不能确保有意义的数据。

将这些约束build立到业务层是一个好主意,但这并不能确保数据库保持不变。 确保数据规则不被违反的唯一方法是在数据库中尽可能最低的级别执行它们。

一个问题是,如果你不得不使用多个版本的SQL Server,那MAX就不会一直工作。 因此,如果您正在处理旧版数据库或涉及多个版本的任何其他情况,最好非常小心。

如上所述,这主要是存储和性能之间的折衷。 至less在大多数情况下。

但是,在n / varchar(n)上selectn / varchar(Max)时至less应该考虑另一个因素。 数据是否被索引(例如,姓氏)? 由于MAX定义被认为是一个LOB,所以定义为MAX的任何东西都不可用于索引。 如果没有索引,则任何涉及作为WHERE子句中谓词的数据的查找都将被强制转换为全表扫描,这是数据查找所能获得的最差的性能。

1)在处理nvarchar(max)和nvarchar(n)时,SQL服务器将不得不使用更多的资源(分配的内存和CPU时间),其中n是特定于该字段的数字。

2)这是什么意思关于performance?

在SQL Server 2005上,我使用15个nvarchar(max)列查询了13000行数据。 我重复定时查询,然后将列更改为nvarchar(255)或更less。

优化前的查询平均为2.0858秒。 更改后的查询平均返回1.90秒。 这是对基本select *查询改进大约184毫秒的时间。 这是8.8%的改善。

3)我的结果与其他一些文章一致,表明有性能差异。 根据您的数据库和查询,改进的百分比可能会有所不同。 如果你没有太多的并发用户或很多logging,那么性能差异对你来说就不是问题。 但是,随着更多logging和并发用户的增加,性能差异将会增加。

我有一个udf填充string,并把输出到varchar(最大)。 如果这是直接使用,而不是callback到正确的大小调整列,performance是非常差。 我最后把udf放到了一个任意长度,而不是依靠udf的所有调用者把string重新转换成更小的大小。

有趣的链接: 为什么使用VARCHAR,当你可以使用TEXT?

这是关于PostgreSQL和MySQL的,所以性能分析是不同的,但是“显性”的逻辑依然存在:为什么强迫自己总是担心一小部分时间相关的东西? 如果你将一个电子邮件地址保存到一个variables中,你可以使用“string”而不是“string限制为80个字符”。

遗留系统支持。 如果你有一个系统正在使用这个数据,而且这个数据有一定的长度,那么这个数据库是一个强制执行这个长度的好地方。 这并不理想,但遗留系统有时并不理想。 = P

如果一行(对于所有列)中的所有数据永远不会合理采用8000或更less的字符,那么数据层的devise应该强制执行此操作。

数据库引擎更有效地将所有内容都保存在blob存储中。 你可以越小越好。 您可以在页面中填充的行越多越好。 只有访问较less的页面时,数据库性能会更好。

我的testing显示,select时有差异。

 CREATE TABLE t4000 (a NVARCHAR(4000) NULL); CREATE TABLE tmax (a NVARCHAR(MAX) NULL); DECLARE @abc4 NVARCHAR(4000) = N'ABC'; INSERT INTO t4000 SELECT TOP 1000000 @abc4 FROM master.sys.all_columns ac1, master.sys.all_columns ac2; DECLARE @abc NVARCHAR(MAX) = N'ABC'; INSERT INTO tmax SELECT TOP 1000000 @abc FROM master.sys.all_columns ac1, master.sys.all_columns ac2; SET STATISTICS TIME ON; SET STATISTICS IO ON; SELECT * FROM dbo.t4000; SELECT * FROM dbo.tmax; 

我能看到的主要缺点是让我们说你有这样的:

哪一个给你关于UI所需数据的最多信息?

这个

  CREATE TABLE [dbo].[BusData]( [ID] [int] IDENTITY(1,1) NOT NULL, [RecordId] [nvarchar](MAX) NULL, [CompanyName] [nvarchar](MAX) NOT NULL, [FirstName] [nvarchar](MAX) NOT NULL, [LastName] [nvarchar](MAX) NOT NULL, [ADDRESS] [nvarchar](MAX) NOT NULL, [CITY] [nvarchar](MAX) NOT NULL, [County] [nvarchar](MAX) NOT NULL, [STATE] [nvarchar](MAX) NOT NULL, [ZIP] [nvarchar](MAX) NOT NULL, [PHONE] [nvarchar](MAX) NOT NULL, [COUNTRY] [nvarchar](MAX) NOT NULL, [NPA] [nvarchar](MAX) NULL, [NXX] [nvarchar](MAX) NULL, [XXXX] [nvarchar](MAX) NULL, [CurrentRecord] [nvarchar](MAX) NULL, [TotalCount] [nvarchar](MAX) NULL, [Status] [int] NOT NULL, [ChangeDate] [datetime] NOT NULL ) ON [PRIMARY] 

或这个?

  CREATE TABLE [dbo].[BusData]( [ID] [int] IDENTITY(1,1) NOT NULL, [RecordId] [nvarchar](50) NULL, [CompanyName] [nvarchar](50) NOT NULL, [FirstName] [nvarchar](50) NOT NULL, [LastName] [nvarchar](50) NOT NULL, [ADDRESS] [nvarchar](50) NOT NULL, [CITY] [nvarchar](50) NOT NULL, [County] [nvarchar](50) NOT NULL, [STATE] [nvarchar](2) NOT NULL, [ZIP] [nvarchar](16) NOT NULL, [PHONE] [nvarchar](18) NOT NULL, [COUNTRY] [nvarchar](50) NOT NULL, [NPA] [nvarchar](3) NULL, [NXX] [nvarchar](3) NULL, [XXXX] [nvarchar](4) NULL, [CurrentRecord] [nvarchar](50) NULL, [TotalCount] [nvarchar](50) NULL, [Status] [int] NOT NULL, [ChangeDate] [datetime] NOT NULL ) ON [PRIMARY] 

一个缺点是你将围绕一个不可预测的variables进行devise,而你可能会忽略而不是利用内部的SQL Server数据结构,逐步地由Row,Page和Stent(Stent)组成。

这让我想到了C语言中的数据结构alignment ,知道alignment通常被认为是Good Thing(TM)。 类似的想法,不同的背景。

页面和范围的 MSDN页面

行溢出数据的 MSDN页面

这将导致性能问题,尽pipe如果数据库很小,它可能永远不会导致任何实际问题。 每个logging将占用硬盘上的更多空间,如果您一次search大量logging,数据库将需要读取更多的磁盘扇区。 例如,一个小logging可能适合50个扇区,大logging可能适合5个。您需要使用大logging从磁盘读取10倍的数据。

这将使屏幕devise变得更加困难,因为你将不再能够预测你的控件应该有多宽。