关于sql server:在SQL中通过VARCHAR选择CHAR的用例是什么?

关于sql server:在SQL中通过VARCHAR选择CHAR的用例是什么?

What are the use cases for selecting CHAR over VARCHAR in SQL?

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


如果所有行的长度接近相同,通常选择CHAR。当长度变化很大时,请选择VARCHAR。由于所有行的长度相同,因此CHAR的速度也可能会更快。

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

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

底线是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。例如,存储性别已知的(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。

好。

另一个用途是在为将数据导出到固定宽度文件而创建的视图中。假设我必须将一些数据导出到平面文件中,以供大型机读取。它是固定宽度(不定界)。我喜欢将数据存储在" staging"表中作为varchar(从而减少了数据库空间),然后使用视图将所有内容都转换为与char等效的字符,其长度与该列固定宽度的宽度相对应。例如:

好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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':

1
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 vs VARCHAR的相关性逐渐降低。


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

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

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

但是除非表很大,否则您可能不会在意性能下降。

记住Djikstra的明智话。早期的性能优化是万恶之源。


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


我认为在您的情况下,可能没有理由不选择Varchar。它为您提供了灵活性,正如许多受访者所提到的那样,现在的表现是如此出色,除非在非常特殊的情况下,我们的普通人(与Google DBA相对)不会注意到两者之间的差异。

当涉及到DB Types时,一个值得注意的有趣事情是sqlite(一种性能非常出色的流行小型数据库)将所有内容作为字符串和类型即时输入数据库。

我一直使用VarChar,通常使它比我可能需要的大得多。例如。正如您所说的,Firstname为50。


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

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

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


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

因此,您会遇到以下一些问题:

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

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

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

名称|性别|城市

鲍勃||洛杉矶

如果未指定性别,则在表中输入Bob,空字符串和Los Angeles。现在,假设您获取了文件,并且文件格式发生了变化,并且不再包含性别,而是过去。

名称|城市

鲍勃|西雅图

现在好了,因为不包括性别,所以我将使用null。 Varchars支持这一点而没有问题。

另一方面,字符不同。您总是必须发送空值。如果您发送空字符串,最终将得到一个带有空格的字段。

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


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


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


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


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

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


使用CHAR(NCHAR)和VARCHAR(NVARCHAR)会在数据库服务器存储数据的方式上带来差异。第一个介绍尾随空白;在SQL SERVER函数中将其与LIKE运算符一起使用时,遇到了问题。因此,我必须始终使用VARCHAR(NVARCHAR)使其安全。

例如,如果我们有一个表TEST(ID INT,Status CHAR(1)),并且您编写了一个函数来列出具有某些特定值的所有记录,如下所示:

1
2
3
4
5
6
CREATE FUNCTION List(@STATUS AS CHAR(1) = '')
RETURNS TABLE
AS
RETURN
SELECT * FROM TEST
WHERE STATUS LIKE '%' + @STATUS '%'

在此函数中,我们希望当我们使用默认参数时,该函数将返回所有行,但实际上并非如此。将@Status数据类型更改为VARCHAR将解决此问题。


推荐阅读