在SQL中选择CHAR而不是VARCHAR的用例是什么?

我意识到,如果我的所有值都是固定宽度的,建议使用CHAR。但是,那又怎样?为了安全起见,为什么不为所有文本字段选择VARCHAR呢?

174000 次浏览

Char更快一点,所以如果你知道一个列有一定的长度,就使用Char。例如,存储(M)ale/(F)emale/(U)nknown表示性别,或者存储2个字符表示美国的一个州。

在计算列值实际所需的大小和为Varchar分配空间时,会有一些小的处理开销,因此如果您确实确定值总是多长,那么最好使用Char并避免命中。

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

一般规则是,如果所有行都接近长度相同,则选择字符。当长度的变化显着时选择VARCHAR(或NVARCHAR)。CHAR也可能更快一些,因为所有的行都是相同的长度。

它因数据库实现而异,但通常,VARCHAR(或NVARCHAR)在实际数据之外使用一个或两个字节的存储空间(用于长度或终止)。因此(假设您使用的是单字节字符集)存储单词"FooBar"

  • CHAR (6) = 6字节
  • VARCHAR (100) = 8字节
  • CHAR (10) = 10字节

对于长度相对相同的数据(两个字符长度差以内),底线是字符 可以和更多空间

请注意: Microsoft SQL对于VARCHAR有2个字节的开销。这可能因DB而异,但通常至少需要1个字节的开销来指示VARCHAR上的长度或EOL。

正如< a href = " https://stackoverflow.com/users/1106569/gavin-towey " >加文< / >在评论中指出的那样:当涉及到多字节字符集时,情况会发生变化,在这种情况下VARCHAR会成为更好的选择。

关于VARCHAR的声明长度的说明:因为它存储了实际内容的长度,所以你不会浪费未使用的长度。因此,在VARCHAR(6), VARCHAR(100), VARCHAR(MAX)中存储6个字符使用相同的存储量。阅读更多关于使用VARCHAR (MAX)时的差异。在VARCHAR中声明最大大小来限制存储的数量。

在评论中AlwaysLearning指出,Microsoft Transact-SQL文档似乎说的正好相反。我认为这是一个错误,或者至少文件不清楚。

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

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

此外,如果您将VARCHAR列更新到比其先前内容更大的大小,则可能会强制数据库重新构建其索引(因为您强制数据库将记录物理地移动到磁盘上)。而对于CHAR列,这种情况永远不会发生。

但是,除非表很大,否则您可能不会关心性能受到的影响。

记住吉布拉的至理名言。早期的性能优化是万恶之源。

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

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

早期性能优化和使用最佳实践类型的规则之间存在差异。如果创建的新表中总是有固定长度的字段,那么使用CHAR是有意义的,在这种情况下应该使用它。这不是早期优化,而是实现经验法则(或最佳实践)。

即-如果你有一个2字母的状态字段,使用CHAR(2)。如果您有一个包含实际州名的字段,请使用VARCHAR。

如果你和我一起工作,你和Oracle一起工作,我可能会让你在几乎所有情况下都使用varcharcharvarchar使用更少的处理能力的假设可能是正确的…现在……但是随着时间的推移,数据库引擎会变得更好,这种通用规则将成为未来的“神话”。

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

最后一个想法:我在使用CHAR时看到了各种各样的问题,人们在应该寻找''的时候寻找'',或者人们在应该寻找'FOO(这里有一堆空格)'的时候寻找'FOO',或者人们没有修整后面的空格,或者Powerbuilder将2000个空格加到它从Oracle过程中返回的值的错误。

除了性能上的好处,CHAR还可以用来表示所有应该的值都是相同的长度,例如,美国州缩写的列。

我会选择varchar,除非列存储固定的值,如美国州代码-这总是2个字符长,有效的美国州代码列表不经常改变:)。

在其他情况下,甚至像存储哈希密码(固定长度),我会选择varchar。

为什么——char类型的列总是用空格填充,这使得列my_column定义为char(5),其中值为'ABC':

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

假的。

这个功能可能会在开发过程中导致许多恼人的bug,并使测试更加困难。

有一些性能上的好处,但是这里有一个没有被提及的好处:行迁移。使用char,您可以提前预订整个空间。假设你有一个char(1000),你存储了10个字符,你会用完所有1000个字符的空间。在varchar2(1000)中,您只能使用10个字符。当您修改数据时,问题就出现了。假设您将列更新到现在包含900个字符。可能是当前块中没有可用的空间来展开varchar。在这种情况下,DB引擎必须将该行迁移到另一个块,并在原始块中创建指向新块中的新行的指针。为了读取这些数据,DB引擎现在必须读取2个block 没有人能含糊地说varchar或char更好。这里有一个时间权衡的空间,并考虑数据是否会更新,特别是如果它有很好的机会会增长

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

在MS SQL 2005中,Varchar(或每个字符需要两个字节的语言,如中文)是可变长度的。如果您在将行写入硬盘后再添加数据,则会将数据定位在与原始行不相邻的位置,并导致数据文件碎片化。这将影响性能。

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

我认为在你的情况下,可能没有理由不选择Varchar。它为您提供了灵活性,正如许多受访者所提到的,现在的性能是这样的,除非在非常特定的情况下,我们普通人(而不是谷歌DBA)不会注意到差异。

当谈到DB类型时,值得注意的一件有趣的事情是sqlite(一个非常受欢迎的迷你数据库,具有相当令人印象深刻的性能)将所有内容作为字符串放入数据库并动态地输入类型。

我总是使用VarChar,通常使它比我可能迫切需要的要大得多。如你所说,为了安全起见,为什么不买50英镑呢?

许多人指出,如果知道值的确切长度,使用CHAR会有一些好处。但是,虽然今天将美国州存储为CHAR(2)很棒,但当您从销售人员那里收到“我们刚刚完成了对澳大利亚的第一笔销售”的消息时,您将陷入痛苦的世界。我总是高估我认为字段需要多长时间,而不是做一个“准确”的猜测来覆盖未来的事件。VARCHAR将在这方面给我更多的灵活性。

如果字段中的所有数据值长度相同,则CHAR占用的存储空间比VARCHAR少。现在,在2009年,800GB的数据库与810GB的数据库(如果您将varchar转换为CHARs)在所有用途和目的上是一样的,但对于短字符串(1或2个字符),CHAR仍然是行业的“最佳实践”。

现在,如果您查看大多数数据库提供的各种各样的数据类型,即使是整数(bit、tiny、int、bigint),也有理由选择其中一种。每次都简单地选择bigint实际上是对字段的目的和用途有点无知。如果一个字段只是以年为单位表示一个人的年龄,那么使用bigint就太夸张了。现在它不一定是“错误的”,但它不是有效的。

但这是一个有趣的争论,随着数据库的改进,可以说CHAR vs VARCHAR的相关性越来越小。

NChar或Char是否比它们的var替代品表现更好?

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

显然,我们都知道,如果我创建一个列为varchar(255)的表(让我们把这个列称为myColumn),并插入一百万行,但每行只在myColumn中放入几个字符,那么表将比我将myColumn创建为char(255)要小得多(存储引擎所需的数据页的总数)。每当我对该表执行操作(DML)并请求大量行的时候,当myColumn是varchar时,它会更快,因为我不需要在末尾的所有“额外”空格周围移动。移动,例如当SQL Server进行内部排序时,例如在不同或合并操作期间,或者如果它在查询计划期间选择合并,等等。Move还可以指将数据从服务器传输到我的本地pc或另一台计算机或任何将要使用数据的地方所花费的时间。

但是使用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)或其他合理的值。它在在线系统中工作得最好,因为您通常只检索或上传少量行,因此必须“rtrim”结果集中的尾随空格,这是一项微不足道的任务,而不必将一个表中的数百万行连接到另一个表中的数百万行。

在在线系统上,CHAR比varchar更有意义的另一个原因是它减少了页面分割。通过使用char,你实际上是在“保留”(和浪费)这个空间,所以如果一个用户后来把更多的数据放到那个列中,SQL已经为它分配了空间。

使用CHAR的另一个原因与第二个原因类似。如果程序员或用户对数百万行进行“批处理”更新,例如在注释字段中添加一些句子,您不会在半夜接到DBA的电话,询问为什么他们的驱动器已满。换句话说,它导致数据库规模的增长更加可预测。

以上就是联机(OLTP)系统从char优于varchar中获益的3种方式。我很少在仓库/分析/OLAP场景中使用char,因为通常你有太多的数据,所有这些char列加起来会浪费大量的空间。

请记住,char会使您的数据库更大,但大多数备份工具都有数据压缩,因此您的备份往往与使用varchar时的大小相同。例如LiteSpeed或RedGate SQL Backup。

另一个用途是在为将数据导出到固定宽度文件而创建的视图中。假设我必须将一些数据导出到一个平面文件中以供主机读取。它是固定宽度(没有分隔)。我喜欢将数据以varchar的形式存储在“staging”表中(从而在数据库中消耗更少的空间),然后使用一个视图将所有内容CAST为它的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只是浪费空间。

希望这有帮助。 杰夫< / p >

碎片。Char会保留空间,而VarChar则不会。为了适应varchar的更新,可以要求页面分割。

使用CHAR (NCHAR)和VARCHAR (NVARCHAR)会在数据库服务器存储数据的方式上带来不同。第一个引入了尾随空格;我在SQL SERVER函数中使用LIKE操作符时遇到了问题。因此,我必须始终使用VARCHAR (NVARCHAR)来确保它的安全性。

例如,如果我们有一个表测试(ID INT,状态CHAR(1)),你写一个函数列出所有具有特定值的记录,如下所示:

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

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

我永远不会使用字符。我和很多人都有过这样的争论,他们总是提出令人厌倦的cliché char更快。我说,快多少?我们在这里讨论的是什么,毫秒,秒,如果是的话,是多少?你是在告诉我,因为有人声称它快了几毫秒,我们就应该在系统中引入大量难以修复的bug ?

这里有一些你会遇到的问题:

每个字段都将被填充,因此您最终得到的代码将永远到处都是RTRIMS。对于较长的字段来说,这也是一种巨大的磁盘空间浪费。

现在假设你有一个典型的例子,一个只有一个字符的char字段,但是这个字段是可选的。如果有人传递一个空字符串到这个字段,它就变成了一个空格。因此,当另一个应用程序/进程查询它时,如果不使用rtrim,它们将得到一个单独的空间。我们有xml文档,文件和其他程序,只显示一个空格,在可选字段和打破东西。

现在你要确保传递给char字段的是空值而不是空字符串。但这不是null的正确用法。这里是null的用法。假设您从供应商那里获得了一个文件

城市名字性别| |

鲍勃| |洛杉矶

如果没有指定性别,则在表中输入Bob、空字符串和Los Angeles。现在让我们假设你得到了文件,它的格式改变了,性别不再包括在内,而是在过去。

城市名称|

鲍勃|西雅图

好吧,现在由于性别不包括在内,我将使用null。Varchars毫无问题地支持这一点。

而夏尔则不同。你总是要发送null。如果你发送空字符串,你将得到一个包含空格的字段。

我可以继续在大约20年的开发过程中修复所有的错误。