我意识到如果我的所有值都是固定宽度,建议使用CHAR。但是,那又怎样?为什么不为所有文本字段选择VARCHAR只是为了安全。
答案 0 :(得分:380)
如果所有行都接近相同的长度,通常选择 CHAR 。当长度变化时,选择 VARCHAR 。 CHAR也可能更快一些,因为所有行的长度都相同。
它因DB实现而异,但通常除了实际数据外,VARCHAR还使用一个或两个以上的存储字节(用于长度或终止)。因此(假设您使用的是一个字节字符集)存储单词“FooBar”
底线 CHAR 可以更快,更多空间效率。
注意:对于VARCHAR,Microsoft SQL有2个字节的开销。这可能因DB而异,但通常需要至少1个字节的开销来指示VARCHAR上的长度或EOL。
正如Gaven在评论中所指出的,如果你使用像UTF8这样的多字节可变长度字符集,那么CHAR存储了存储字符数所需的最大字节数。因此,如果UTF8最多需要3个字节来存储字符,那么即使只存储latin1字符,CHAR(6)也将固定为18个字节。所以在这种情况下,VARCHAR成为一个更好的选择。
答案 1 :(得分:63)
如果您正在与我合作并且您正在使用Oracle,我可能会让您在几乎所有情况下都使用varchar
。假设char
使用比varchar
更少的处理能力可能是真的......现在......但数据库引擎会随着时间的推移而变得更好,这种一般规则会构成未来的“神话” ”。
另一件事:我从未见过性能问题,因为有人决定选择varchar
。您将更好地利用编写良好代码的时间(减少对数据库的调用)和高效的SQL(索引如何工作,优化器如何做出决策,为什么exists
通常比in
更快...)。
最后的想法:我已经看到使用CHAR
的各种问题,人们在寻找''时应该寻找'',或者人们在寻找'FOO'时他们应该寻找' FOO(这里有一堆空格)',或者没有修剪尾随空白的人,或者Powerbuilder的错误,它们从Oracle程序返回的值增加了2000个空格。
答案 2 :(得分:29)
除了性能优势之外,CHAR
还可用于指示所有值的长度相同,例如美国州缩写的列。
答案 3 :(得分:18)
Char更快一些,所以如果你有一个你知道的列将是一定的长度,请使用char。例如,存储(M)ale /(F)emale /(U)nknown为性别,或2个字符为美国州。
答案 4 :(得分:16)
答案 5 :(得分:9)
有一些性能优势,但这里没有提到:行迁移。使用char,你可以预先保留整个空间。所以我们说你有一个char(1000),你存储了10个字符,你将耗尽所有1000个空格字符。在varchar2(1000)中,您将只使用10个字符。修改数据时出现问题。假设您将列更新为包含900个字符。扩展varchar的空间可能在当前块中不可用。在这种情况下,数据库引擎必须将行迁移到另一个块,并将原始块中的指针指向新块中的新行。要读取此数据,数据库引擎现在必须读取2个块 没有人可以暧昧地说varchar或char更好。有一个时间权衡的空间,并考虑数据是否会更新,特别是如果它很有可能会增长。
答案 6 :(得分:8)
早期性能优化与使用最佳实践类型的规则之间存在差异。如果要创建新表,其中您将始终具有固定长度字段,则使用CHAR是有意义的,在这种情况下您应该使用它。这不是早期优化,而是实施经验法则(或最佳实践)。
即。 - 如果您有2个字母的州字段,请使用CHAR(2)。如果您有一个包含实际状态名称的字段,请使用VARCHAR。
答案 7 :(得分:8)
我会选择varchar,除非该列存储固定值,如美国州代码 - 总是2个字符长且有效的美国州代码列表不会经常更改:)。
在所有其他情况下,即使存储哈希密码(固定长度),我也会选择varchar。
为什么 - char类型列总是用空格来实现,这使得列 my_column 定义为char(5),其中值'ABC'在比较中:
my_column = 'ABC' -- my_column stores 'ABC ' value which is different then 'ABC'
假。
此功能可能会在开发过程中导致许多恼人的错误,并使测试更加困难。
答案 8 :(得分:6)
如果该字段中的所有数据值都相同,则CHAR占用的存储空间少于VARCHAR。现在也许在2009年,如果你将VARCHAR转换为CHAR,800GB数据库对于所有意图和目的都是相同的,但是对于短字符串(1或2个字符),CHAR仍然是业界“最佳实践”。
现在,如果你看一下大多数数据库提供的各种数据类型,即使只有整数(bit,tiny,int,bigint),也有理由选择其中一种。每次只选择bigint实际上对该领域的目的和用途有点无知。如果一个字段只代表一个年龄的人,那么bigint就是矫枉过正。现在它不一定是“错误的”,但效率不高。
但它是一个有趣的论点,随着数据库随着时间的推移而改善,可以认为CHAR与VARCHAR相关性较低。
答案 9 :(得分:4)
我支持Jim McKeeth的评论。
此外,如果您的表只有CHAR列,则索引和全表扫描会更快。基本上,如果只有CHAR列,优化器将能够预测每条记录的大小,同时需要检查每个VARCHAR列的大小值。
此外,如果将VARCHAR列更新为大于其先前内容的大小,则可能会强制数据库重建其索引(因为您强制数据库在磁盘上物理移动记录)。使用CHAR列时永远不会发生。
但除非你的桌子很大,否则你可能不会关心性能打击。
记住Djikstra的明智之词。早期的性能优化是万恶之源。
答案 10 :(得分:3)
很多人都指出,如果你知道使用CHAR的值的确切长度有一些好处。但是,虽然今天将美国各州以CHAR(2)的形式存储起来非常棒,但当您从销售中得到“我们刚刚向澳大利亚进行首次销售”的消息时,您处于一个痛苦的世界。我总是高估我认为领域需要多长时间,而不是做出“确切”的猜测以涵盖未来的事件。 VARCHAR将为我提供更多这方面的灵活性。
答案 11 :(得分:2)
我永远不会使用字符。我和很多人进行了这场辩论,他们总是提出陈述更快的陈词滥调。好吧,我说,快多少?我们在这里谈论什么,毫秒,秒,如果有多少?你告诉我,因为有人声称它的速度提高了几毫秒,我们应该引入大量难以修复的系统错误吗?
以下是您将遇到的一些问题:
每个字段都将被填充,因此您最终会得到永远存在RTRIMS的代码。对于较长的磁场来说,这也是一个巨大的磁盘空间浪费。
现在假设你有一个只有一个字符的char字段的典型例子,但该字段是可选的。如果有人将空字符串传递给该字段,则它变为一个空格。因此,当另一个应用程序/进程查询它时,如果它们不使用rtrim,它们将获得一个单独的空间。我们已经有xml文档,文件和其他程序,只显示一个空格,在可选字段和中断。
所以现在你必须确保将空值而不是空字符串传递给char字段。但这不是null的正确用法。这是null的用法。假设您从供应商那里获得了一个文件
名称|性别|城
鲍勃||洛杉矶如果未指定性别,则输入Bob,将空字符串和洛杉矶空入表中。现在让我们说你得到了文件及其格式的变化,性别不再包括在内,但已经过去了。
名称|城
鲍勃|西雅图
现在好了,因为不包括性别,我会使用null。变种人毫无问题地支持这一点。
另一方面,Char与众不同。你总是要发送null。如果您发送空字符串,最终会得到一个包含空格的字段。
我可以继续处理从字母和大约20年的开发中解决的所有错误。
答案 12 :(得分:2)
碎片。 Char预留空间,而VarChar则没有。可能需要页面拆分以适应对varchar的更新。
答案 13 :(得分:2)
我认为在你的情况下,可能没有理由不选择Varchar。它为您提供了灵活性,正如许多响应者所提到的,现在表现如此,除非在非常具体的情况下,我们的凡人(与谷歌DBA相反)不会注意到差异。
有关DB类型的一个值得注意的事情是sqlite(一个流行的迷你数据库,具有相当令人印象深刻的性能)将所有内容作为字符串放入数据库并随时输入。
我总是使用VarChar,并且通常比我需要的要大得多。例如。 50为名字,正如你所说,为什么不只是为了安全。
答案 14 :(得分:2)
这是经典的空间与性能之间的权衡。
在MS SQL 2005中,Varchar(或每个字符需要两个字节即中文的lanuagues的NVarchar)是可变长度的。如果在将行写入硬盘后添加到该行,则会将数据放在原始行的不可靠位置,并导致数据文件碎片化。这会影响性能。
因此,如果空间不是问题,那么Char的性能会更好,但如果你想保持数据库大小不变,那么varchars会更好。
答案 15 :(得分:2)
计算列值的实际所需大小并为Varchar分配空间时会有一些小的处理开销,所以如果你肯定确定该值总是多长时间,最好使用Char并避免使用击中。
答案 16 :(得分:1)
使用varchar值时,SQL Server每行需要额外的2个字节来存储有关该列的一些信息,而如果使用char,则不需要 所以,除非你
答案 17 :(得分:0)
在某些SQL数据库中,VARCHAR将填充到其最大大小以优化偏移量,这是为了加快全表扫描和索引。
因此,与CHAR(200)相比,使用VARCHAR(200)不会节省任何空间
答案 18 :(得分:0)
使用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将解决问题。