为什么我会在SQL中selectCHAR over VARCHAR?

我意识到,如果我所有的值都是固定宽度,build议使用CHAR。 但是,什么? 为什么不只是selectVARCHAR的所有文本字段只是为了安全。

如果所有的行将接近相同的长度,一般selectCHAR 。 当长度变化很大时,selectVARCHAR 。 由于所有行的长度相同,CHAR也可能会快一点。

它因数据库实现而异,但通常VARCHAR除了实际的数据之外还使用一到两个字节的存储空间(用于长度或终止)。 所以(假设你正在使用一个字节的字符集)存储单词“FooBar”

  • CHAR(6)= 6字节(无开销)
  • VARCHAR(10)= 8个字节(2个字节的开销)
  • CHAR(10)= 10个字节(4个字节的开销)

对于相对长度相同的数据(两个字符之间的长度差异),底线是CHAR可以更快和更有效率

注意 :Microsoft SQL对于VARCHAR有2个字节的开销。 这可能会有所不同,从数据库到数据库,但通常至less有1个字节的开销,需要指出一个VARCHAR的长度或EOL。

正如Gaven在注释中指出的那样,如果您使用的是像UTF8这样的多字节,可变长度的字符集,则CHAR将存储存储字符数所需的最大字节数。 所以如果UTF8最多需要3个字节来存储一个字符,那么即使只存储了latin1字符,CHAR(6)也会被固定为18个字节。 所以在这种情况下VARCHAR成为一个更好的select。

如果你正在和我一起工作,而且你正在和Oracle合作,那么我几乎可以在任何情况下使用varchar 。 假设char使用比varchar更less的处理能力可能是真实的…现在…但是数据库引擎会随着时间的推移而变得更好,这种通用规则会造就未来的“神话”。

另一件事:我从来没有见过性能问题,因为有人决定去与varchar 。 你将更好地利用你的时间编写好的代码(对数据库的调用更less)和高效的SQL(索引如何工作,优化器如何做决定,为什么exists比通常更快)。

最后的想法:我已经看到了使用CHAR各种问题,人们在寻找的时候寻找的人,或者当他们应该寻找'FOO'的时候寻找'FOO'的人(这里有一堆空间) “或者不修剪尾随空白的人,或者使用Powerbuilder将缺陷添加到2000个空白到从Oracle过程返回的值。

除了性能优势之外, CHAR还可以用来表示所有的值应该是相同的长度,例如美国州名缩写的列。

要存储的数据:“ABCD”


 Char(4) takes 4b varchar(4) takes 6b or Char(40) takes 40b varchar(40) takes 6b or Char(400) takes 400b varchar(400) takes 6b 

字符有点快,所以如果你有一列,你知道将是一定的长度,使用字符。 例如,存储(M)ale /(F)emale /(U),对于性别而言是已知的,或者对于美国州而言是2个字符。

NChar或Char是否performance出更好的select?

伟大的问题。 在某些情况下,简单的答案是肯定的。 我们来看看是否可以解释。

显然,我们都知道,如果我创build了一个varchar(255)列的表(我们称这个列为myColumn),并插入一百万行,但每行只有几个字符到myColumn中,表将会小得多存储引擎所需的数据页数)比如果我创buildmyColumn为char(255)。 任何时候我在这个表上执行一个操作(DML)并请求很多行时,当myColumn是varchar时会更快,因为我不必在最后移动所有这些“额外”空间。 移动,就像在SQL Server进行内部sorting时一样,例如在独立或联合操作期间,或者在查询计划期间select合并等。移动也可能意味着将数据从服务器获取到本地电脑或其他计算机或将要消耗的地方。

但是使用varchar会有一些开销。 SQL Server必须使用两个字节的指示器(开销)来知道每行的特定行myColumn具有多less个字节。 这不是多余的2字节提出的问题,它是必须“解码”在每一行myColumn中的数据的长度。

根据我的经验,最有意义的是在查询中joinchar的列而不是varchar。 例如表格的主键,或者其他将被索引的列。 在人口统计表上的CustomerNumber,或在解码表上的CodeID,或者在订单表上的OrderNumber。 通过使用char,查询引擎可以更快速地执行连接,因为它可以执行直接指针运算(确定性地),而不必在读取页面时将其指针移动可变数量的字节。 我知道我最后一句话可能会让你失去知觉。 SQL Server中的连接基于“谓词”的概念。 谓词是一个条件。 例如myColumn = 1或OrderNumber <500。

因此,如果SQL Server正在执行DML语句,并且要连接的谓词或“键”是固定长度(char),则查询引擎不必执行尽可能多的工作来将表中的行与另一张桌子。 不需要查看数据在行中的时间,然后沿着string查找结尾。 所有这些都需要时间。

现在要记住,这可能很容易实施。 我已经看到在线系统中用于主键字段的字符。 宽度必须保持小,即字符(15)或合理的东西。 而且它在在线系统中效果最好,因为通常只需要检索或插入less量的行,所以不得不在结果集中“rtrim”尾随空格是一件小事,而不是必须将数百万从一个表到另一个表上的数百万行的行。

另一个原因CHAR在线系统上的varchar有意义的是,它减less了页面拆分。 通过使用char,实质上是“保留”(和浪费)该空间,所以如果用户稍后出现并将更多数据放入该列中,SQL已经为其分配了空间。

使用CHAR的另一个原因类似于第二个原因。 如果程序员或用户对数百万行进行了“批量”更新,例如在笔记字段中添加了一些句子,半夜中您就不会接到DBA的电话,不知道为什么他们的驱动器已满。 换句话说,它导致了数据库大小的更可预测的增长。

所以这些是3种方式联机(OLTP)系统可以从char over varchar中受益。 我几乎没有在仓库/分析/ OLAP情况下使用char,因为通常你有太多的数据,所有这些char列可以加起来浪费很多空间。

请记住,字符可以使您的数据库更大,但大多数备份工具有数据压缩,所以你的备份往往是大约相同的大小,如果你已经使用varchar。 例如LiteSpeed或RedGate SQL Backup。

另一个用途是创build用于将数据导出到固定宽度文件的视图。 比方说,我必须将一些数据导出到一个平面文件,以供大型机读取。 它是固定的宽度(不分隔)。 我喜欢将数据作为varchar存储在我的“staging”表中(因此在我的数据库上占用的空间较less),然后使用视图将CAST的所有内容都转换为chartypes,其长度对应于该列的固定宽度的宽度。 例如:

 create table tblStagingTable ( pkID BIGINT (IDENTITY,1,1), CustomerFirstName varchar(30), CustomerLastName varchar(30), CustomerCityStateZip varchar(100), CustomerCurrentBalance money ) insert into tblStagingTable (CustomerFirstName,CustomerLastName, CustomerCityStateZip) ('Joe','Blow','123 Main St Washington, MD 12345', 123.45) create view vwStagingTable AS SELECT CustomerFirstName = CAST(CustomerFirstName as CHAR(30)), CustomerLastName = CAST(CustomerLastName as CHAR(30)), CustomerCityStateZip = CAST(CustomerCityStateZip as CHAR(100)), CustomerCurrentBalance = CAST(CAST(CustomerCurrentBalance as NUMERIC(9,2)) AS CHAR(10)) SELECT * from vwStagingTable 

这很酷,因为内部我的数据占用较less的空间,因为它使用varchar。 但是,当我使用DTS或SSIS,甚至只是从SSMS剪切和粘贴到记事本,我可以使用视图,并获得正确数量的尾随空格。 在DTS中,我们曾经有过一个叫做“function”的function,该死的我忘记了,我觉得它被称为“build议列”或什么的。 在SSIS中,你不能再那么做了,你必须沉闷地定义平面文件连接pipe理器。 但是既然你有了视图设置,SSIS就可以知道每一列的宽度,并且在构build数据stream任务时可以节省大量的时间。

所以底线…使用varchar。 使用char的原因有很less,只是出于性能原因。 如果你有一个拥有数百万行数的系统,如果谓词是确定性的(char),你会看到一个明显的区别,但是对于大多数使用char的系统来说,只是浪费空间。

希望有所帮助。 杰夫

有性能上的好处,但这里还没有提到:行迁移。 用字符,你提前预留整个空间。所以让我们说你有一个字符(1000),你存储10个字符,你会用尽所有1000个字符的空间。 在varchar2(1000)中,只能使用10个字符。 修改数据时出现问题。 假设您将列更新为现在包含900个字符。 当前块中可能没有扩展varchar的空间。 在这种情况下,数据库引擎必须将该行迁移到另一个块,并将原始块中的指针指向新块中的新行。 要读取这些数据,数据库引擎现在必须读取2个数据块。
没有人可以不分青红皂白地说,varchar或char更好。 有一个时间折衷的空间,并考虑数据是否会更新,特别是如果有很大的可能会增长。

早期性能优化和使用最佳实践types的规则是有区别的。 如果你正在创build新的表格,你将永远有一个固定长度的字段,这是有道理的使用CHAR,你应该在这种情况下使用它。 这不是早期的优化,而是实施一个经验法则(或最佳实践)。

即 – 如果你有一个2字母的状态字段,使用CHAR(2)。 如果您有一个具有实际状态名称的字段,请使用VARCHAR。

我会selectvarchar除非列存储像美国州代码固定值 – 这是总是2个字符长,有效的美国国家代码列表不会经常更改:)。

在其他情况下,即使像存储哈希密码(这是固定长度),我会selectvarchar。

为什么 – 字符types列总是用空格来实现的,这使得列my_column被定义为在比较内部被赋值为'ABC'的char(5):

 my_column = 'ABC' -- my_column stores 'ABC ' value which is different then 'ABC' 

假。

这个function可能会在开发过程中导致许多恼人的错误,并使testing变得更加困难。

如果该字段中的所有数据值都是相同的长度,则CHAR将占用比VARCHAR更less的存储空间。 现在也许在2009年,如果将VARCHAR转换为CHAR,800GB数据库对于所有意图和目的都是一样的,但对于短string(1或2个字符),CHAR仍然是行业“最佳实践”。

现在,如果您查看大多数数据库提供的各种数据types(即使是整数(bit,tiny,int,bigint)),也有理由相互select。 每次简单地selectbigint实际上是对该领域的目的和用途有点无知。 如果一个领域只是年代的一个人的年龄,那么一个大的东西就是矫枉过正的。 现在不一定是“错误的”,但效率不高。

但是它是一个有趣的论点,随着时间的推移数据库也在不断改进,可以认为CHAR vs VARCHAR的关联性不大。

我支持吉姆·麦克凯思的评论。

此外,索引和全表扫描速度更快,如果您的表只有CHAR列。 基本上,优化器将能够预测每条logging有多大,如果它只有CHAR列,而需要检查每个VARCHAR列的大小值。

此外,如果将VARCHAR列更新为大于其以前内容的大小,则可以强制数据库重build其索引(因为您强制数据库将该logging物理移动到磁盘上)。 而CHAR列则永远不会发生。

但是除非你的桌子很大,否则你可能不会关心性能。

记住Djikstra的智慧话语。 早期的性能优化是万恶之源。

这是经典的空间与性能的权衡。

在MS SQL 2005中,Varchar(或NVarchar,用于需要两个字节(即每个字符,即中文)的远程节点)是可变长度的。 如果在写入硬盘后将其添加到行中,则会将数据定位到原始行的不连续位置,并导致数据文件碎片化。 这会影响性能。

所以,如果空间不是问题,那么Char对性能更好,但是如果你想保持数据库的大小,那么varchars更好。

在计算列值的实际需要大小和为Varchar分配空间时会有一些小的处理开销,所以如果您确定该值始终存在多长时间,最好使用Char来避免命中。

我想你的情况可能没有理由不selectVarchar。 它给了你灵活性,正如一些响应者已经提到的那样,现在的performance是这样的,除非在特定的情况下,我们更多的凡人(而不是Google DBA)不会注意到这种差异。

在数据库types方面值得注意的一个有趣的事情是sqlite(一个stream行的微型数据库,性能相当不错)把所有的东西放到数据库中作为一个string,并在飞行中types。

我总是使用VarChar,通常比我可能需要的要大得多。 例如。 50为名,正如你所说,为什么不只是为了安全。

很多人都指出,如果你知道使用CHAR的确切长度值有一些好处。 但是,如果今天将美国的州储存为CHAR(2),那么当你从销售中得到“我们刚刚首次销售到澳大利亚”的消息时,你处在一个痛苦的世界里。 我总是高估我认为领域需要多长时间,而不是为未来事件做出“确切”的猜测。 VARCHAR会给我在这方面更多的灵活性。

当使用varchar值时,SQL Server每行需要另外2个字节来存储关于该列的一些信息,而如果使用char,则不需要这样,除非你

碎片。 Char预留空间,而VarChar则没有。 页面拆分可能需要适应更新到varchar。

在一些SQL数据库中,VARCHAR会被填充到其最大值以优化偏移量,这是为了加快全表扫描和索引。

因此,与CHAR(200)相比,使用VARCHAR(200)不会节省空间,

使用CHAR(NCHAR)和VARCHAR(NVARCHAR)会导致数据库服务器存储数据的方式不同。 第一个引入尾随空白; 在SQL SERVER函数中使用LIKE运算符时遇到问题。 所以我必须一直使用VARCHAR(NVARCHAR)来保证安全。

例如,如果我们有一个表TEST(ID INT,Status CHAR(1)) ,并且您编写了一个函数来列出具有某个特定值的所有logging,如下所示:

 CREATE FUNCTION List(@Status AS CHAR(1) = '') RETURNS TABLE AS RETURN SELECT * FROM TEST WHERE Status LIKE '%' + @Status '%' 

在这个函数中,我们期望当我们把默认参数的时候,这个函数会返回所有的行,但实际上并没有。 将@Status数据types更改为VARCHAR将解决此问题。