282

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

4

19 回答 19

394

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

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

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

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

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

正如Gaven在评论中指出的那样:当涉及到多字节字符集时,情况会发生变化,这是 VARCHAR 成为更好选择的情况。

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

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

于 2008-09-12T18:22:06.927 回答
69

如果您与我一起工作并且您正在与 Oracle 一起工作,我可能会让您varchar在几乎所有情况下都使用它。char使用较少处理能力的假设varchar可能是真实的......现在......但随着时间的推移,数据库引擎会变得更好,这种一般规则会成为未来的“神话”。

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

最后的想法:我已经看到了使用的各种问题CHAR,人们在应该寻找 '' 时寻找 '',或者人们在应该寻找 'FOO (bunch of spaces here)' 时寻找 'FOO' ,或者没有修剪尾随空格的人,或者 Powerbuilder 的错误将多达 2000 个空格添加到它从 Oracle 过程返回的值。

于 2008-09-12T18:42:23.070 回答
32

除了性能优势之外,CHAR还可用于指示所有值的长度相同,例如,用于美国州缩写的列。

于 2008-09-12T18:57:55.530 回答
19

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

于 2008-09-12T18:17:17.187 回答
18

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 的另一个原因与第二个原因类似。如果程序员或用户对数百万行进行“批量”更新,例如在注释字段中添加一些句子,您将不会在半夜接到 DBA 的电话,想知道为什么他们的驱动器已满。换句话说,它会导致数据库大小的更可预测的增长。

因此,这些是在线 (OLTP) 系统可以从 char 而非 varchar 中受益的 3 种方式。我几乎从不在仓库/分析/OLAP 场景中使用 char,因为通常您拥有如此多的数据,以至于所有这些 char 列加起来会浪费大量空间。

请记住,char 可以使您的数据库变得更大,但大多数备份工具都具有数据压缩功能,因此您的备份往往与使用 varchar 的大小大致相同。例如 LiteSpeed 或 RedGate SQL 备份。

另一个用途是在为将数据导出到固定宽度文件而创建的视图中。假设我必须将一些数据导出到一个平面文件以供大型机读取。它是固定宽度(未定界)。我喜欢将数据作为 varchar 存储在我的“暂存”表中(从而在我的数据库上消耗更少的空间),然后使用视图将所有内容转换为它的 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 的系统来说,只是在浪费空间。

希望有帮助。杰夫

于 2011-03-04T01:31:29.577 回答
9

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

于 2008-09-13T01:28:41.643 回答
8

早期性能优化和使用最佳实践类型的规则是有区别的。如果您要创建始终具有固定长度字段的新表,则使用 CHAR 是有意义的,您应该在这种情况下使用它。这不是早期优化,而是实施经验法则(或最佳实践)。

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

于 2008-09-12T18:37:52.893 回答
8

我会选择 varchar ,除非该列存储像美国州代码这样的固定值——它总是 2 个字符长并且有效的美国州代码列表不会经常更改:)。

在所有其他情况下,即使像存储散列密码(固定长度)一样,我也会选择 varchar。

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

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

错误的。

功能可能会在开发过程中导致许多烦人的错误,并使测试变得更加困难。

于 2008-09-12T20:11:03.997 回答
6

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

现在,如果您查看大多数数据库甚至仅针对整数(bit、tiny、int、bigint)提供的各种数据类型,就有理由选择其中一种。每次都简单地选择 bigint 实际上有点不了解该领域的目的和用途。如果一个字段仅代表一个人的年龄(以年为单位),那么 bigint 就过分了。现在它不一定是“错误的”,但它不是有效的。

但这是一个有趣的论点,并且随着数据库随着时间的推移而改进,可以说 CHAR 与 VARCHAR 确实变得不那么相关了。

于 2009-01-21T00:07:14.267 回答
6

我永远不会使用字符。我与很多人进行过辩论,他们总是提出陈词滥调,即 char 更快。好吧,我说,快多少?我们在这里谈论的是什么,毫秒,秒,如果有的话是多少?你告诉我是因为有人声称它快了几毫秒,我们应该在系统中引入大量难以修复的错误?

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

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

现在假设您有一个典型示例,即只有一个字符的 char 字段,但该字段是可选的。如果有人将一个空字符串传递给该字段,它就会变成一个空格。因此,当另一个应用程序/进程查询它时,如果他们不使用 rtrim,他们会得到一个空格。我们有 xml 文档、文件和其他程序,在可选字段中只显示一个空格并破坏事物。

因此,现在您必须确保将空值而不是空字符串传递给 char 字段。但这不是 null 的正确用法。这里是null的使用。假设您从供应商处获得文件

姓名|性别|城市

鲍勃||洛杉矶

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

名称|城市

鲍勃|西雅图

好吧,既然不包括性别,我会使用null。Varchars 支持这一点没有问题。

另一方面,Char 则不同。你总是必须发送空值。如果您曾经发送空字符串,您最终会得到一个包含空格的字段。

在大约 20 年的开发过程中,我可以继续解决我必须从字符中修复的所有错误。

于 2018-04-13T18:12:00.727 回答
4

我支持 Jim McKeeth 的评论。

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

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

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

记住吉克斯特拉的话。早期的性能优化是万恶之源。

于 2008-09-12T18:28:11.670 回答
4

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

于 2008-11-04T21:38:19.823 回答
3

我认为在您的情况下,可能没有理由不选择 Varchar。它为您提供了灵活性,并且正如许多受访者所提到的,现在的性能如此之高,除非在非常特殊的情况下,我们这些普通人(与 Google DBA 不同)不会注意到差异。

当谈到 DB Types 时,值得注意的一件有趣的事情是 sqlite(一个流行的迷你数据库,性能相当令人印象深刻)将所有内容作为字符串和动态类型放入数据库中。

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

于 2008-09-18T13:01:25.337 回答
2

在计算列值的实际需要大小并为 Varchar 分配空间时,有一些小的处理开销,因此如果您确定该值将始终保持多长时间,则最好使用 Char 并避免命中。

于 2008-09-12T18:18:33.410 回答
2

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

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

因此,如果空间不是问题,那么 Char 的性能更好,但如果您想减小数据库大小,那么 varchars 会更好。

于 2008-09-14T18:21:53.040 回答
2

碎片化。Char 保留空间,而 VarChar 没有。可能需要页面拆分以适应对 varchar 的更新。

于 2013-10-25T12:11:30.990 回答
1

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

于 2008-09-12T18:22:05.973 回答
0

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

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

于 2008-09-12T18:30:52.100 回答
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 将解决此问题。

于 2014-11-25T03:59:30.270 回答