Sql 始终使用nvarchar(MAX)有什么缺点吗?

Sql 始终使用nvarchar(MAX)有什么缺点吗?,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,在SQL Server 2005中,将所有字符字段设置为nvarchar(MAX)而不是显式指定长度(例如nvarchar(255))是否有任何缺点?(除了一个明显的问题,即您无法在数据库级别限制字段长度)我发现的唯一问题是我们在SQL Server 2005上开发应用程序,在一个实例中,我们必须支持SQL Server 2000。我刚刚了解到,SQL Server 2000不喜欢varchar或nvarchar的MAX选项,这是一个公平的问题,除了明显的 缺点可能包括: 性能影响 查询优化器使

在SQL Server 2005中,将所有字符字段设置为nvarchar(MAX)而不是显式指定长度(例如nvarchar(255))是否有任何缺点?(除了一个明显的问题,即您无法在数据库级别限制字段长度)

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

缺点可能包括:

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

“1.数据库扩展和页面中的空间分配是灵活的。因此,在使用update向字段添加信息时,如果新数据比先前插入的数据长,则数据库必须创建一个指针。这会导致数据库文件变得支离破碎=几乎所有方面的性能都会降低,从索引到删除、更新和插入。"

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

这里有一篇好文章:

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

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

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

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


我有一个udf,它填充字符串并将输出放入varchar(max)。如果直接使用此选项,而不是将要调整的列强制转换回适当的大小,则性能非常差。我最终使用一个大音符将udf设置为任意长度,而不是依赖udf的所有调用者将字符串重新转换为较小的大小。

这将导致性能问题,尽管如果您的数据库很小,则可能永远不会导致任何实际问题。如果您同时搜索大量记录,则每个记录将占用硬盘上更多的空间,并且数据库将需要读取磁盘上更多的扇区。例如,一个小记录可以容纳50个扇区,而一个大记录可以容纳5个扇区。您需要读取10倍的扇区使用大记录从磁盘获取大量数据。

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

有趣的链接:


这是关于PostgreSQL和MySQL的,所以性能分析是不同的,但逻辑是“明确性”“仍然是这样:为什么总是强迫自己在一小部分时间里担心一些相关的事情呢?”?如果将电子邮件地址保存到变量中,则应使用“字符串”而不是“限制为80个字符的字符串”。

一个问题是,如果必须使用多个版本的SQL Server,则最大值将不总是有效。因此,如果您使用的是遗留数据库或涉及多个版本的任何其他情况,您最好非常小心。

有时您希望数据类型对其中的数据具有某种意义

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


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

如果您知道该字段将在一个设定的范围内(例如,5到10个字符),这是个坏主意。我想只有在我不确定长度的情况下才会使用max。例如,电话号码永远不会超过一定数量的字符

你能诚实地说你对表中每个字段的大致长度要求都不确定吗

我确实明白你的意思。当然,有些领域我当然会考虑使用VARCHAR(MAX)。

有趣的是,这篇文章总结得很好:

当 列数据条目差别很大。 当 列数据条目差异很大, 并且大小可能超过8000字节


这是一个安全级别。

将其视为另一个安全级别。您可以设计没有外键关系的表—完全有效—并确保关联实体完全存在于业务层。但是,外键被认为是良好的设计实践,因为它们添加了另一个约束级别,以防业务层出现问题。字段大小限制也是如此,并且不使用varchar MAX.

传统系统支持。如果您有一个正在使用数据的系统,并且预期该数据会有一定的长度,那么数据库是实施该长度的好地方。这并不理想,但遗留系统有时并不理想=P

如果一行(所有列)中的所有数据永远不会合理地使用8000个或更少的字符,那么数据层的设计应该强制执行这一点

数据库引擎的效率要高得多,可以将所有内容保存在blob存储中。限制行越小越好。一页中可以填充的行越多越好。当数据库必须访问时,它的性能会更好
            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]
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
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;