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

问题描述 投票:321回答:22

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

sql sql-server sql-server-2005
22个回答
142
投票

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

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

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

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


4
投票

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

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


3
投票

一个问题是,如果您必须使用多个版本的SQL Server,MAX将无法始终工作。因此,如果您正在处理遗留数据库或涉及多个版本的任何其他情况,您最好小心。


3
投票

如上所述,它主要是存储和性能之间的权衡。至少在大多数情况下。

但是,在n / varchar(n)上选择n / varchar(Max)时,至少应考虑另外一个因素。数据是否会被索引(例如,姓氏)?由于MAX定义被认为是LOB,因此定义为MAX的任何内容都不可用于索引。如果没有索引,任何涉及数据作为WHERE子句中的谓词的查找都将被强制进入全表扫描,这是数据查找的最差性能。


2
投票

1)当处理nvarchar(max)vs nvarchar(n)时,SQL服务器必须利用更多资源(分配的内存和cpu时间),其中n是特定于字段的数字。

2)这对性能意味着什么?

在SQL Server 2005上,我从具有15个nvarchar(max)列的表中查询了13,000行数据。我重复计时查询,然后将列更改为nvarchar(255)或更少。

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

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


1
投票

我有一个udf填充字符串并将输出放到varchar(max)。如果直接使用它而不是回流到适合调整的色谱柱的尺寸,则性能非常差。我最终把udf放到一个带有大音符的任意长度,而不是依赖于udf的所有调用者将字符串重新转换为更小的大小。


1
投票

有趣的链接:Why use a VARCHAR when you can use TEXT?

它是关于PostgreSQL和MySQL的,因此性能分析是不同的,但“显性”的逻辑仍然存在:为什么要强迫自己总是担心在一小部分时间内相关的事情?如果您将电子邮件地址保存到变量,则使用“字符串”而不是“限制为80个字符的字符串”。


1
投票

遗留系统支持。如果您的系统正在使用数据并且预计它具有一定的长度,则数据库是强制执行长度的好地方。这并不理想,但遗留系统有时并不理想。 = P


1
投票

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

数据库引擎可以更有效地将所有内容保存在blob存储之外。您可以越小越好。您可以在页面中填充的行越多越好。当数据库必须访问较少的页面时,它的性能会更好。


1
投票

我的测试显示选择时存在差异。

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;

0
投票

我能看到的主要缺点是,假设你有这个:

哪一个为您提供有关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]

47
投票

这是一个公平的问题,他确实陈述了明显的......

缺点可能包括:

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

“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


0
投票

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

这让我想到了C中的data structure alignment,并且知道对齐通常被认为是Good Thing(TM)。类似的想法,不同的背景。

Pages and Extents的MSDN页面

Row-Overflow Data的MSDN页面


-1
投票

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


-3
投票

它将使屏幕设计更难,因为您将无法再预测控件的宽度。


28
投票

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

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

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


24
投票

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

  1. 存储在nvarchar(MAX)字段中的100个字符将存储在nvarchar(100)字段中与100个字符不同 - 数据将以内联方式存储,您将不会有“行外”读取和写入数据的开销。所以不用担心。
  2. 如果大小大于4000,数据将自动存储在“行外”,这就是您想要的。所以也不用担心。

然而...

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

结论:

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


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

13
投票

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


8
投票

不使用max或text字段的原因是,即使使用SQL Server Enterprise Edition,也无法执行online index rebuilds即REBUILD WITH ONLINE = ON。


4
投票

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


4
投票

当您知道该字段将处于设定范围时(例如5到10个字符),这是个坏主意。如果我不确定长度是多少,我想我只会使用max。例如,电话号码永远不会超过一定数量的字符。

你真的可以说你对表中每个字段的大致长度要求都不确定吗?

我确实明白了 - 有一些我肯定会考虑使用varchar(max)的字段。

有趣的是,MSDN docs总结得很好:

当列数据条目的大小变化很大时,请使用varchar。当列数据条目的大小变化很大时,使用varchar(max),大小可能超过8,000个字节。

an interesting discussion on the issue here

© www.soinside.com 2019 - 2024. All rights reserved.