SQL varchar列长度的最佳实践

ess*_*kar 273 mysql sql sql-server postgresql

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

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

另一方面,当您选择创建varchar列时,MSSQL服务器将默认长度值设置为50.这让我想到了.为什么50?它只是一个随机数,还是基于平均列长,或者是什么?

它也可能 - 或者可能是 - 不同的SQL服务器实现(如MySQL,MSSQL,Postgres,...)具有不同的最佳列长度值.

a_h*_*ame 226

我所知道的DBMS没有任何"优化"可以使长度比长度不是2 VARCHAR2^n长度更好max.

我认为早期的SQL Server版本实际上对VARCHAR长度为255的处理不同于具有更高最大长度的版本.我不知道是否仍然如此.

对于几乎所有DBMS,所需的实际存储仅由您放入其中的字符数决定,而不是max您定义的长度.因此,从存储的角度来看(也很可能是性能问题),无论是否将列声明为VARCHAR(100)或者,都没有任何区别VARCHAR(500).

您应该maxVARCHAR列提供的长度视为一种约束(或业务规则)而不是技术/物理事物.

对于PostgreSQL,最好的设置是在text没有长度限制的情况下使用,并且CHECK CONSTRAINT限制字符数量以满足您的业务需求.

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

这同样可以应用于Oracle和其他人 - 在Oracle中它将是VARCHAR(4000)代替text.

我不知道是否有一个物理存储之间的差异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 nvarcharvarchar列的声明长度对性能产生巨大影响.

  • 实际上,VARCHAR(255)和VARCHAR(500)之间存在差异,即使您在此列中放置了1个字符.在行末尾附加的值将是一个整数,用于存储实际存储数据的长度.在VARCHAR(255)的情况下,它将是1字节整数.在VARCHAR(500)的情况下,它将是2个字节.这是一个小差异,但人们应该意识到这一点.我手边没有任何数据如何影响性能,但我认为它很小,不值得研究. (31认同)
  • @NB你指的是哪个RDBMS?SQL Server?对性能有影响.[N] VARCHAR(max)执行速度略慢于[N] VARCHAR(n).我最近[提到这个网站](http://rusanu.com/2010/03/22/performance-comparison-of-varcharmax-vs-varcharn/).对于我所知道的,PostgreSQL也是如此. (4认同)
  • @NB:这就是我所指的 SQL Server 的“魔法” 255 值。感谢您的澄清。 (2认同)
  • 更改 varchar 长度不会重写表。它只是像 CHECK CONSTRAINT 一样检查整个表的约束长度。如果你增加长度没有什么可做的,只是下一次插入或更新将接受更大的长度。如果减少长度并且所有行都通过新的较小约束,除了允许下一次插入或更新仅写入新长度之外,Pg 不会采取任何进一步的操作。 (2认同)

Ari*_*iel 64

VARCHAR(255)VARCHAR(2)采取准确的空间相同数量的磁盘上!因此,限制它的唯一原因是,如果您特别需要它更小.否则全部为255.

具体来说,在进行排序时,较大的列会占用更多空间,因此如果这会损害性能,那么您需要担心它并使它们变小.但是,如果您只从该表中选择1行,那么您可以将它们全部设为255并且无关紧要.

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

  • 为什么不把它们都变成`VARCHAR(MAX)`?在建模数据库时,空间不是唯一的考虑因素.您正在建模的域应该驱动数据类型和大小. (6认同)
  • @Oded`VARCHAR(MAX)`与`varchar(255)`或`varchar(65535)`不同 - varchar max是一种`text`数据类型.而且就你的观点而言 - 如果他知道"他正在建模的领域"是什么,他就不会问这个问题.很显然,他不知道他的数据会有多大,而且我向他保证,制作它的全尺寸不会伤害任何东西. (5认同)
  • @Ariel:索引还有一些问题和限制需要考虑.当所有四列都是`VARCHAR(255)`时,你不能有一个`(a,b,c,d)`索引. (4认同)

Kit*_*Kit 48

每当我设置一个新的SQL表时,我都会有同样的感觉,即2 ^ n更"均匀"......但总结一下这里的答案,只需定义varchar(2 ^ n)就不会对存储空间产生重大影响.甚至varchar(MAX).

也就是说,在设置高varchar()限制时,您仍应该预见到对存储和性能的潜在影响.例如,假设您创建了一个varchar(MAX)列来保存带有全文索引的产品描述.如果99%的描述只有500个字符长,然后突然有人用维基百科文章替换所述描述,您可能会注意到意外的重要存储和性能命中.

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
Run Code Online (Sandbox Code Playgroud)

  • 可视化获胜!我在设计时经常提到那个块.太有用了. (11认同)
  • 喜欢这些例子。很有帮助 (5认同)

Ode*_*ded 30

最佳值是适用于基础域中定义的数据的值.

对于某些域,VARCHAR(10)适用于该Name属性,因为其他域VARCHAR(255)可能是最佳选择.


Jon*_*ack 14

添加到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
Run Code Online (Sandbox Code Playgroud)

不要忘记长度字节和可空字节,所以:

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

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

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

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

希望这可以帮助 :)


one*_*hen 6

请务必咨询您的业务领域专家.如果那就是你,那就找一个行业标准.例如,如果相关域名是自然人的姓氏(姓氏),那么对于英国企业,我会访问英国Govtalk数据标准目录以获取个人信息,并发现姓氏将介于1到35个字符之间.


小智 6

我最近没有检查过这一点,但我知道过去使用 Oracle 时,JDBC 驱动程序会在查询执行期间保留一块内存来保存返回的结果集。内存块的大小取决于列定义和获取大小。因此 varchar2 列的长度会影响保留的内存量。几年前,这给我带来了严重的性能问题,因为我们总是使用 varchar2(4000)(当时的最大值),并且垃圾收集的效率比现在低得多。