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


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


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


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


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

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

CHAR(6) = 6字节(无开销) VARCHAR(100) = 8字节(2字节开销) CHAR(10) = 10字节(4字节浪费)

最重要的是,对于相对相同长度的数据(在两个字符长度差以内),CHAR可以更快、更节省空间。

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

正如Gaven在评论中指出的:当涉及到多字节字符集时,情况会发生变化,在这种情况下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,我可能会让您在几乎所有情况下都使用varchar。char比varchar使用更少的处理能力的假设可能是正确的…现在……但是随着时间的推移,数据库引擎会变得更好,这种通用规则将成为未来的“神话”。

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

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


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


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

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

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

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

假的。

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


There are performance benefits, but here is one that has not been mentioned: row migration. With char, you reserve the entire space in advance.So let's says you have a char(1000), and you store 10 characters, you will use up all 1000 charaters of space. In a varchar2(1000), you will only use 10 characters. The problem comes when you modify the data. Let's say you update the column to now contain 900 characters. It is possible that the space to expand the varchar is not available in the current block. In that case, the DB engine must migrate the row to another block, and make a pointer in the original block to the new row in the new block. To read this data, the DB engine will now have to read 2 blocks. No one can equivocally say that varchar or char are better. There is a space for time tradeoff, and consideration of whether the data will be updated, especially if there is a good chance that it will grow.


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

在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替代品表现更好?

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

Obviously we all know that if I create a table with a column of varchar(255) (let's call this column myColumn) and insert a million rows but put only a few characters into myColumn for each row, the table will be much smaller (overall number of data pages needed by the storage engine) than if I had created myColumn as char(255). Anytime I do an operation (DML) on that table and request alot of rows, it will be faster when myColumn is varchar because I don't have to move around all those "extra" spaces at the end. Move, as in when SQL Server does internal sorts such as during a distinct or union operation, or if it chooses a merge during it's query plan, etc. Move could also mean the time it takes to get the data from the server to my local pc or to another computer or wherever it is going to be consumed.

但是使用varchar会有一些开销。SQL Server必须使用一个双字节指示器(开销),在每一行上,知道特定行的myColumn中有多少字节。造成问题的不是额外的2个字节,而是必须“解码”myColumn中每一行的数据长度。

In my experiences it makes the most sense to use char instead of varchar on columns that will be joined to in queries. For example the primary key of a table, or some other column that will be indexed. CustomerNumber on a demographic table, or CodeID on a decode table, or perhaps OrderNumber on an order table. By using char, the query engine can more quickly perform the join because it can do straight pointer arithmetic (deterministically) rather than having to move it's pointers a variable amount of bytes as it reads the pages. I know I might have lost you on that last sentence. Joins in SQL Server are based around the idea of "predicates." A predicate is a condition. For example myColumn = 1, or 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

This is cool because internally my data takes up less space because it's using varchar. But when I use DTS or SSIS or even just a cut and paste from SSMS to Notepad, I can use the view and get the right number of trailing spaces. In DTS we used to have a feature called, damn I forget I think it was called "suggest columns" or something. In SSIS you can't do that anymore, you have to tediously define the flat file connection manager. But since you have your view setup, SSIS can know the width of each column and it can save alot of time when building your data flow tasks.

所以底线是……使用varchar。使用char的原因很少,而且仅仅是出于性能方面的考虑。如果您有一个有数亿行的系统,如果谓词是确定性的(char),您将看到一个显著的差异,但对于大多数系统来说,使用char只是浪费空间。

希望这能有所帮助。 杰夫


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


使用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将解决该问题。


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

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

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

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

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

城市名字性别| |

鲍勃| |洛杉矶

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

城市名称|

鲍勃|西雅图

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

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

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