SQL varchar列长度的最佳做法


288

每次设置新的SQL表或varchar向现有表添加新列时,我都想知道一件事:的最佳价值是什么length

假设您有一个名为nametype 的列varchar。因此,您必须选择长度。我想不出一个名称> 20个字符,但您永远不会知道。但是我总是使用四舍五入到下一个2 ^ n数字,而不是使用20。在这种情况下,我将选择32作为长度。我这样做是因为从计算机科学家的角度来看,数字2 ^ n even在我看来比其他数字要多,而我只是假设下面的体系结构可以更好地处理这些数字。

另一方面,例如,当您选择创建varchar列时,MSSQL服务器会将默认长度值设置为50。那让我开始思考。为什么是50?只是一个随机数,还是基于平均列长,还是什么?

不同的SQL Server实现(例如MySQL,MSSQL,Postgres等)也可能具有(也可能是)不同的最佳列长值。

Answers:


238

据我所知,没有DBMS有任何“优化”可以使长度VARCHAR为a的2^n性能比max长度为2的幂的性能更好。

我认为早期的SQL Server版本实际上将VARCHAR255的长度与最大长度较高的长度区别对待。我不知道情况是否仍然如此。

对于几乎所有的DBMS,所需的实际存储空间仅取决于您放入其中的字符数,而不取决于max您定义的长度。因此,从存储的角度(也可能是性能的角度)而言,将列声明为VARCHAR(100)还是都没有任何区别VARCHAR(500)

您应该max将为VARCHAR列提供的长度看作是一种约束(或业务规则),而不是技术/实际的事物。

对于PostgreSQL来说,最好的设置是使用text不受长度限制CHECK CONSTRAINT的字符集,它将字符数限制为您的业务需要。

如果该要求发生变化,则更改检查约束比更改表要快得多(因为无需重写表)

可以对Oracle和其他对象应用相同的方法-在Oracle中,它VARCHAR(4000)不是,text而是。

我不知道SQL Server VARCHAR(max)VARCHAR(500)SQL Server 之间是否存在物理存储差异。但varchar(max)与相比,使用显然会对性能产生影响varchar(8000)

看到此链接(由Erwin Brandstetter发表评论)

编辑2013-09-22

关于bigown的评论:

在9.2之前的Postgres版本中(我写初始答案时不可用),对列定义的更改确实重写了整个表,请参见例如此处。从9.2开始,情况就不再如此,快速测试证实,增加具有120万行的表的列大小实际上仅用了0.5秒。

对于Oracle来说,从更改大表的varchar列所需的时间来看,这似乎也是正确的。但是我找不到任何参考。

对于MySQL ,手册上写着在大多数情况下,ALTER TABLE为原始表创建一个临时副本 ”。我自己的测试证实了:ALTER TABLE在具有120万行的表上运行(与我对Postgres的测试相同)以增加列的大小需要1.5分钟。但是,在MySQL中,您不能使用“替代方法”来使用检查约束来限制列中的字符数。

对于SQL Server,我对此没有找到明确的声明,但是增加varchar列的大小(同样是上表的120万行表)的执行时间表明没有重写。

编辑2017-01-24

似乎我(至少部分地)对SQL Server错误。请参阅Aaron Bertrand的答案,该答案表明a nvarcharvarcharcolumn 的声明长度对性能有很大的影响。


34
实际上,即使您在此列中放入1个字符,VARCHAR(255)和VARCHAR(500)之间也存在差异。该行末尾附加的值将是一个整数,用于存储所存储数据的实际长度。如果是VARCHAR(255),它将是1个字节的整数。如果是VARCHAR(500),则为2个字节。这是一个很小的差异,但是应该意识到这一点。我手头上没有任何数据会影响性能,但是我认为它是如此之小,不值得研究。
NB

1
@NB:这就是我指的是SQL Server的“魔术” 255值。感谢您的澄清。
a_horse_with_no_name

4
@NB您指的是哪个RDBMS?SQL Server?对性能有影响。[N] VARCHAR(max)的执行速度略慢于[N] VARCHAR(n)。我最近被引荐到此站点。就我所知,PostgreSQL并非如此。
Erwin Brandstetter

@ErwinBrandstetter:感谢您的链接。看起来varchar(max)可能更像是Oracle的CLOB
a_horse_with_no_name

1
更改varchar长度不会重写该表。它只是严格按照CHECK CONSTRAINT检查整个表的约束长度。如果增加了长度,则无需执行任何操作,仅下一次插入或更新将接受更大的长度。如果减小长度,并且所有行都通过新的较小约束,则Pg除了允许下一次插入或更新仅写入新长度之外,不会采取任何其他措施。
Maniero

69

VARCHAR(255)VARCHAR(2)占用完全相同的磁盘空间!因此,限制它的唯一原因是您是否有特殊的需要,以使其更小。否则,将它们全部设置为255。

具体来说,在进行排序时,较大的列会占用更多空间,因此,如果这会影响性能,则需要担心它并使其变小。但是,如果您只从该表中选择1行,则只需将它们全部设置为255,就没有关系了。

请参阅:MySQL的最佳varchar大小是多少?


7
为什么不全部制作它们VARCHAR(MAX)呢?在对数据库建模时,空间不是唯一的考虑因素。您正在建模的域应驱动数据类型和大小。
奥德

6
@Oded VARCHAR(MAX)不等于varchar(255)或-varchar varchar(65535)max是一种text数据类型。和你要说的-如果他知道他正在建模的“领域”,他就不会问这个问题。显然,他不知道自己的数据将变得多大,而且我向他保证,将其全尺寸存储不会对您造成任何伤害。
Ariel

4
@Ariel:索引也有问题和限制。(a,b,c,d)当所有四列都为时,您将没有索引VARCHAR(255)
ypercubeᵀᴹ

@ypercube的确如此,如果您的列需要索引,则需要更加注意大小。但是大多数列都不需要索引,因此大多数时候您不必担心它。
Ariel

我认为,如果我们知道确切的值,我更喜欢使用char。同时,如果仍然可以使用,我使用varchar并保留255,因为它是动态内存分配,因此您不必担心会占用的大小
Faris Rayhan

54

每当我建立一个新的SQL表时,我都会以相同的方式感觉到2 ^ n更加“均匀” ...但是总结一下这里的答案,只需定义varchar(2 ^ n)就不会对存储空间产生重大影响。甚至是varchar(MAX)。

就是说,在设置较高的varchar()限制时,您仍然应该预期对存储和性能的潜在影响。例如,假设您创建了一个varchar(MAX)列来保存带有全文索引的产品描述。如果99%的描述仅500个字符长,然后突然您发现有人用Wikipedia文章替换了这些描述,那么您可能会注意到意料之外的重大存储和性能损失。

Bill Karwin还需要考虑的另一件事

这可能会对性能产生影响:在MySQL中,临时表和MEMORY表将VARCHAR列存储为固定长度的列,并填充为其最大长度。如果您将VARCHAR列设计为远远大于所需的最大大小,则将消耗更多的内存。这会影响缓存效率,排序速度等。

基本上,只是提出合理的业务约束和稍大一点的错误。正如@onedaywhen指出的那样,英国的姓氏通常在1到35个字符之间。如果您决定将其设为varchar(64),则不会伤到任何东西……除非您要存储此人的姓氏(据说最长为666个字符)。在这种情况下,也许varchar(1028)更有意义。

如果有用的话,这是从varchar 2 ^ 5到2 ^ 10填充后的样子:

varchar(32)     Lorem ipsum dolor sit amet amet.

varchar(64)     Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donecie

varchar(128)    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donecie
                vestibulum massa. Nullam dignissim elementum molestie. Vehiculas

varchar(256)    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donecie
                vestibulum massa. Nullam dignissim elementum molestie. Vehiculas
                velit metus, sit amet tristique purus condimentum eleifend. Quis
                que mollis magna vel massa malesuada bibendum. Proinde tincidunt

varchar(512)    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donecie
                vestibulum massa. Nullam dignissim elementum molestie. Vehiculas
                velit metus, sit amet tristique purus condimentum eleifend. Quis
                que mollis magna vel massa malesuada bibendum. Proinde tincidunt
                dolor tellus, sit amet porta neque varius vitae. Seduse molestie
                lacus id lacinia tempus. Vestibulum accumsan facilisis lorem, et
                mollis diam pretium gravida. In facilisis vitae tortor id vulput
                ate. Proin ornare arcu in sollicitudin pharetra. Crasti molestie

varchar(1024)   Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donecie
                vestibulum massa. Nullam dignissim elementum molestie. Vehiculas
                velit metus, sit amet tristique purus condimentum eleifend. Quis
                que mollis magna vel massa malesuada bibendum. Proinde tincidunt
                dolor tellus, sit amet porta neque varius vitae. Seduse molestie
                lacus id lacinia tempus. Vestibulum accumsan facilisis lorem, et
                mollis diam pretium gravida. In facilisis vitae tortor id vulput
                ate. Proin ornare arcu in sollicitudin pharetra. Crasti molestie
                dapibus leo lobortis eleifend. Vivamus vitae diam turpis. Vivamu
                nec tristique magna, vel tincidunt diam. Maecenas elementum semi
                quam. In ut est porttitor, sagittis nulla id, fermentum turpist.
                Curabitur pretium nibh a imperdiet cursus. Sed at vulputate este
                proin fermentum pretium justo, ac malesuada eros et Pellentesque
                vulputate hendrerit molestie. Aenean imperdiet a enim at finibus
                fusce ut ullamcorper risus, a cursus massa. Nunc non dapibus vel
                Lorem ipsum dolor sit amet, consectetur Praesent ut ultrices sit

31

最佳值是适合基础域中定义的数据的值。

对于某些域,属性VARCHAR(10)是正确的Name,对于其他域,VARCHAR(255)可能是最佳选择。


15

添加到a_horse_with_no_name的答案中,您可能会发现感兴趣的以下内容...

将列声明为VARCHAR(100)还是VACHAR(500)都没有任何区别。

-- try to create a table with max varchar length
drop table if exists foo;
create table foo(name varchar(65535) not null)engine=innodb;

MySQL Database Error: Row size too large.

-- try to create a table with max varchar length - 2 bytes for the length
drop table if exists foo;
create table foo(name varchar(65533) not null)engine=innodb;

Executed Successfully

-- try to create a table with max varchar length with nullable field
drop table if exists foo;
create table foo(name varchar(65533))engine=innodb;

MySQL Database Error: Row size too large.

-- try to create a table with max varchar length with nullable field
drop table if exists foo;
create table foo(name varchar(65532))engine=innodb;

Executed Successfully

不要忘记长度字节和可为空的字节,因此:

name varchar(100) not null 将是1个字节(长度)+最多100个字符(拉丁1)

name varchar(500) not null 将是2个字节(长度)+最多500个字符(拉丁1)

name varchar(65533) not null 将是2个字节(长度)+最多65533个字符(latin1)

name varchar(65532) 将是2个字节(长度)+最多65532个字符(latin1)+1个空字节

希望这可以帮助 :)


您正在使用MySQL,而有关MSSQL的问题是
Bogdan Mart


3

我最近没有检查过,但是我过去与Oracle一起知道,JDBC驱动程序将在查询执行期间保留一块内存来保存返回的结果集。内存块的大小取决于列定义和访存大小。因此,varchar2列的长度会影响保留的内存量。几年前,这对我造成了严重的性能问题,因为我们始终使用varchar2(4000)(当时的最大值),垃圾回收的效率远不如今天。


-2

从某种意义上说你是对的,尽管少于2 ^ 8个字符的任何内容仍将注册为字节数据。

如果您将导致VARCHAR <255保留为空的基本字符解释为占用相同的空间量。

255是一个很好的基线定义,除非您特别希望减少过多的输入。


尽管少于2 ^ 8个字符的任何内容仍将注册为数据字节 ”-错误。数据库仅存储VARCHAR类型中提供的字符数。声明列时,不会“注册”,保留或初始化任何空间。
a_horse_with_no_name
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.