Answers:
如果所有行的长度接近相同,通常选择CHAR。当长度变化很大时,请选择VARCHAR。由于所有行的长度相同,因此CHAR也可能会快一些。
它因数据库的实现而异,但是通常VARCHAR除了实际数据外,还会使用一个或两个以上的字节存储空间(用于长度或终止)。因此(假设您使用的是一个字节的字符集),则存储单词“ FooBar”
底线是CHAR可以更快,更节省空间对于相对相同长度(两个字符的长度差异以内)的数据。
注意:Microsoft SQL对于VARCHAR有2个字节的开销。这可能因数据库而异,但是通常至少需要1个字节的开销来指示VARCHAR上的长度或EOL。
正如Gaven在评论中指出的那样,如果您使用的是多字节可变长度字符集(如UTF8),则CHAR将存储存储字符数所需的最大字节数。因此,如果UTF8最多需要3个字节来存储字符,那么即使仅存储latin1字符,CHAR(6)也将固定为18个字节。因此,在这种情况下,VARCHAR成为更好的选择。
如果您正在与我合作,并且正在与Oracle合作,那么我可能会让您varchar
在几乎每种情况下都使用。假设char
使用的处理能力比varchar
现在还低……但是……随着时间的流逝,数据库引擎会变得更好,而这种一般规则将成为未来的“神话”。
另一件事:我从未见过性能问题,因为有人决定使用varchar
。您将可以更好地利用时间编写优质的代码(对数据库的调用更少)和有效的SQL(索引如何工作,优化器如何制定决策,为什么要这样做)。exists
比平时更快in
...)。
最终的想法:我已经看到了使用的各种问题CHAR
,人们在应该寻找“”时寻找“”,或者人们在寻找“ FOO(此处是一堆空格)”时寻找“ FOO”的人们,或者没有修剪尾随空白的人,或者是Powerbuilder的错误,使它从Oracle过程返回的值最多增加2000个空白。
除性能优势外,CHAR
还可用于指示所有值都应具有相同的长度,例如,美国州缩写的列。
CHAR
,则必须确保约束折扣填充。
Char快一点,因此如果您知道的列长度一定,请使用char。例如,存储性别已知的(M)ale /(F)emale /(U)n,或存储美国州的2个字符。
NChar或Char的性能是否优于其var替代品?
好问题。在某些情况下,简单的答案是肯定的。让我们看看这是否可以解释。
显然,我们都知道,如果我创建一个具有varchar(255)列的表(我们将此列命名为myColumn),并插入一百万行,但每行只向myColumn放入几个字符,则该表将小得多(总的来说) (如果我将myColumn创建为char(255),则存储引擎所需的数据页数)。每当我在该表上执行操作(DML)并请求很多行时,当myColumn为varchar时,它就会更快,因为我不必在末尾移动所有这些“多余”空间。移动,例如在SQL Server执行内部排序(例如在不重复或合并操作期间)或在查询计划期间选择合并等时。
但是使用varchar会有一些开销。SQL Server必须在每个行上使用两个字节的指示符(开销)来知道特定行的myColumn中有多少个字节。并不是出现问题的多余2个字节,而是必须“解码”每行myColumn中数据的长度。
以我的经验,在要加入查询的列上使用char而不是varchar是最有意义的。例如,表的主键或其他将要建立索引的列。人口统计表上的CustomerNumber或解码表上的CodeID或订单表上的OrderNumber。通过使用char,查询引擎可以更快地执行联接,因为它可以(确定性地)执行直接指针算术,而不必在读取页面时将其指针移动可变数量的字节。我知道我可能在最后一句话上失去了你。SQL Server中的联接基于“谓词”的概念。谓词是条件。例如myColumn = 1或OrderNumber <500。
因此,如果SQL Server正在执行DML语句,并且谓词或要连接的“键”是固定长度(char),则查询引擎不必执行太多工作就可以将一个表中的行与另一个表中的行进行匹配。另一张桌子。不必找出行中的数据有多长,然后沿着字符串查找结尾即可。所有这一切都需要时间。
现在请记住,这很容易实现不佳。我已经看到char用于在线系统中的主键字段。宽度必须保持较小,即char(15)或合理的值。它在联机系统中效果最好,因为您通常只检索或上载少量行,因此不得不“整理”结果集中的尾随空格是一项微不足道的任务,而不是必须加入数百万行从一个表的行到另一表的数百万行。
联机系统上CHAR比varchar有意义的另一个原因是它减少了页面拆分。通过使用char,您实际上是在“保留”(并浪费)该空间,因此,如果用户以后出现,并将更多数据放入该列中,SQL已经为其分配了空间。
使用CHAR的另一个原因与第二个原因相似。如果程序员或用户对数百万行进行了“批处理”更新,例如在note字段中添加了一些句子,那么您就不会在半夜接到来自DBA的电话,不知道为什么他们的驱动器已满。换句话说,它导致数据库大小的增长更加可预测。
因此,在线(OLTP)系统可以从char受益于varchar的3种方式。在仓库/分析/ OLAP场景中,我几乎不会使用char,因为通常您拥有如此多的数据,所有这些char列都可能导致大量浪费的空间。
请记住,char可以使您的数据库更大,但是大多数备份工具都具有数据压缩功能,因此您的备份大小通常与使用varchar相同。例如LiteSpeed或RedGate SQL Backup。
另一个用途是在为将数据导出到固定宽度文件而创建的视图中。假设我必须将一些数据导出到平面文件中,以供大型机读取。它是固定宽度(不定界)。我喜欢将数据作为“ varchar”存储在“ staging”表中(从而减少了数据库空间),然后使用视图将所有内容都转换为等同于char的字符,其长度对应于该列固定宽度的宽度。例如:
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
这很酷,因为在内部,由于使用varchar,我的数据占用的空间更少。但是,当我使用DTS或SSIS甚至只是从SSMS剪切并粘贴到记事本时,我都可以使用该视图并获得正确数量的尾随空格。在DTS中,我们曾经有一个功能,该死的,我忘了我认为它被称为“建议列”之类的东西。在SSIS中,您不能再这样做了,您必须繁琐地定义平面文件连接管理器。但是,由于您已经设置了视图,因此SSIS可以知道每一列的宽度,并且可以在构建数据流任务时节省大量时间。
因此,底线...使用varchar。使用char的原因非常少,仅出于性能原因。如果您的系统具有数以百万计的行,那么如果谓词是确定性的(char),您会看到明显的不同,但是对于大多数使用char的系统来说,这只是浪费空间。
希望能有所帮助。杰夫
有性能上的好处,但是这里没有提到:行迁移。使用char时,您可以预先保留整个空间。假设您有一个char(1000),并且存储了10个字符,则将用完所有1000个字符。在varchar2(1000)中,您将仅使用10个字符。当您修改数据时,问题就来了。假设您将列更新为现在包含900个字符。扩展varchar的空间可能在当前块中不可用。在这种情况下,DB引擎必须将该行迁移到另一个块,并在原始块中创建一个指向新块中新行的指针。要读取此数据,数据库引擎现在将必须读取2个块。
没有人可以明确地说varchar或char更好。有时间权衡的余地,并考虑是否要更新数据,尤其是在很有可能增长数据的情况下。
早期的性能优化与使用最佳实践类型的规则有所不同。如果要创建新表,在该表中始终具有固定长度的字段,则使用CHAR是有意义的,在这种情况下,应使用它。这不是早期的优化,而是实施经验法则(或最佳实践)。
即-如果您有2个字母的状态字段,请使用CHAR(2)。如果您有一个带有实际状态名称的字段,请使用VARCHAR。
我会选择varchar,除非该列存储的固定值如美国州代码-始终为2个字符,并且有效的美国州代码列表不会经常更改:)。
在其他所有情况下,即使像存储哈希密码(固定长度)一样,我也会选择varchar。
为什么-char类型的列始终用空格填充,这使得my_column列定义为char(5),其内部比较值为:ABC:
my_column = 'ABC' -- my_column stores 'ABC ' value which is different then 'ABC'
假。
此功能可能会在开发过程中导致许多令人讨厌的错误,并使测试更加困难。
如果您在该字段中的所有数据值长度相同,则CHAR占用的存储空间将比VARCHAR少。现在,也许在2009年,如果将VARCHAR转换为CHAR,则800GB的数据库在所有意图和用途上都与810GB相同,但是对于短字符串(1个或2个字符),CHAR仍然是业界“最佳实践”。
现在,如果您查看大多数数据库提供的各种各样的数据类型,甚至仅为整数(位,tiny,int,bigint)提供的数据,就有理由选择一种。每次简单地选择bigint实际上实际上是对该领域的目的和用途的了解。如果一个字段仅代表一个人的年龄(岁),那么bigint就是过大了。现在,它不一定是“错误的”,但是效率不高。
但这是一个有趣的论点,并且随着数据库的不断改进,可以说CHAR与VARCHAR的相关性降低了。
我支持吉姆·麦基思的评论。
此外,如果表只有CHAR列,则索引编制和全表扫描速度更快。基本上,如果优化器仅具有CHAR列,则优化器将能够预测每个记录的大小,同时它需要检查每个VARCHAR列的大小值。
此外,如果将VARCHAR列更新为大于其先前内容的大小,则可能会强制数据库重建其索引(因为您已强制数据库将记录物理移动到磁盘上)。而使用CHAR列则永远不会发生。
但是除非表很大,否则您可能不会在意性能下降。
记住吉克斯特拉的明智之言。早期的性能优化是万恶之源。
CHAR
列时,索引也需要更新。在这方面,更新VARCHAR或CHAR列没有区别。考虑更新FOO
到BAR
。
我永远不会使用字符。我与许多人进行过辩论,他们总是提出陈腐的陈词滥调,即char更快。好吧,我说,快多少?我们在这里谈论的是毫秒,秒,如果是多少秒?您是在告诉我,因为有人声称它要快几毫秒,我们应该在系统中引入大量难以修复的错误吗?
因此,您会遇到以下一些问题:
每个字段都会被填充,因此您最终将永远拥有到处都是RTRIMS的代码。对于较长的字段,这也是巨大的磁盘空间浪费。
现在,假设您有一个典型的char字段示例,该字符字段只有一个字符,但该字段是可选的。如果有人将一个空字符串传递给该字段,它将成为一个空格。因此,当另一个应用程序/进程查询它时,如果不使用rtrim,它们将得到一个空格。我们有xml文档,文件和其他程序,仅在可选字段中显示一个空格,然后破坏内容。
因此,现在必须确保将空值而不是空字符串传递给char字段。但这不是null的正确使用。这是null的使用。假设您从供应商处获取文件
名称|性别|城市
鲍勃||洛杉矶
如果未指定性别,则在表中输入Bob,空字符串和Los Angeles。现在,假设您获取了文件,并且文件格式发生了变化,并且不再包含性别,而是过去。
名称|城市
鲍勃|西雅图
现在好了,因为不包括性别,所以我将使用null。Varchars支持这一点而没有问题。
另一方面,字符不同。您总是必须发送空值。如果您发送空字符串,最终将得到一个带有空格的字段。
在大约20年的开发过程中,我可以继续解决chars必须修复的所有错误。
在某些SQL数据库中,VARCHAR将被填充到其最大大小以优化偏移量,这是为了加快全表扫描和索引的速度。
因此,与CHAR(200)相比,使用VARCHAR(200)不会节省任何空间
使用CHAR(NCHAR)和VARCHAR(NVARCHAR)带来了数据库服务器存储数据的方式的差异。第一个介绍尾随空白;在SQL SERVER函数中与LIKE运算符一起使用时遇到问题。因此,我必须始终使用VARCHAR(NVARCHAR)使其安全。
例如,如果我们有一个表TEST(ID INT,Status CHAR(1)),并且您编写了一个函数来列出具有某些特定值的所有记录,如下所示:
CREATE FUNCTION List(@Status AS CHAR(1) = '')
RETURNS TABLE
AS
RETURN
SELECT * FROM TEST
WHERE Status LIKE '%' + @Status '%'
在此函数中,我们希望当我们使用默认参数时,该函数将返回所有行,但实际上并非如此。将@Status数据类型更改为VARCHAR将解决此问题。