【发布时间】:2011-06-19 15:57:37
【问题描述】:
我在最近不得不使用的数据库中遇到了很多 VARCHAR(1) 字段。我翻了个白眼:显然设计师一点头绪都没有。但也许我是需要学习一些东西的人。是否有任何可以想象的理由使用 VARCHAR(1) 数据类型而不是 CHAR(1)?我认为 RDMS 会自动将一个转换为另一个。
数据库是 MS SQL 2K5,但在当时是从 Access 演变而来的。
【问题讨论】:
标签: sql sql-server-2005 types
我在最近不得不使用的数据库中遇到了很多 VARCHAR(1) 字段。我翻了个白眼:显然设计师一点头绪都没有。但也许我是需要学习一些东西的人。是否有任何可以想象的理由使用 VARCHAR(1) 数据类型而不是 CHAR(1)?我认为 RDMS 会自动将一个转换为另一个。
数据库是 MS SQL 2K5,但在当时是从 Access 演变而来的。
【问题讨论】:
标签: sql sql-server-2005 types
是的,这是有道理的。
更容易在语言中定义。将 varchar 定义为允许 1-8000 比说它需要 2+ 或 3+ 到 8000 更一致且更容易。
VARCHAR(1) 的 VARying CHARacter 方面正是如此。它可能不是存储的最佳选择,但传达了一个特定的含义,即数据是 1 个字符(课堂代码)或空白(外部活动)而不是 NULL(未知/尚未分类)。
存储在这方面的作用很小 - 查看 CHAR(1) 的数据库模式,您几乎会认为它必须始终具有 1 个字符值,例如信用卡必须有 16 位数字。对于某些可以为一个或可选为无的数据,情况并非如此。
对于那些说三态 [ 1-char | 0 字符 | NULL ] 完全没用。它允许 SQL 语句,如:
select activity + '-' + classroom
from ...
如果你使用 char(1)+NULL 会更困难,它可以传达相同的信息,但有细微的差别。
【讨论】:
Nobody 是正确的all 时间。
Coalesce 与 null 和长度大于零的值一起使用更加明确和灵活。
AFAIK,不。
aVARCHAR(1)需要3字节存储(存储大小为输入数据的实际长度+2字节。Ref。
CHAR(1) 需要 1 个字节。
从存储角度来看:经验法则是,如果小于或等于 5 个字符,请考虑使用固定长度的 char 列。
避免使用 varchar(1) 的一个原因(除了它们传达糟糕的设计推理,IMO)是在使用 Linq2SQL 时:LINQ to SQL and varchar(1) fields
【讨论】:
char(4) + '-x' 有不需要的空格。是的,您可以修剪,但这会增加复杂性,而不仅仅是使用 varchar(4)。
bug。 bug 修复后会使该点无效,并且 extremely 已本地化为 VS2008。 social.msdn.microsoft.com/Forums/en-US/linqtosql/thread/…, social.msdn.microsoft.com/Forums/en/linqtosql/thread/…
Varchar(1) 违反了最小惊讶规则,一无所获,并且在存储一个额外的位以指示实际使用的字符数时确实有成本(无论多么轻微)。我想不出任何情况下这将是一个合乎逻辑的设计选择。
char(1) 的列中对该表来说并不昂贵。不可能是因为它可以包含的唯一值的数量是有限的。但是,使用char(1) PK 将外键约束删除到该表可能会很昂贵,如果我没记错的话,无论您使用的是char 还是varchar,都必须这样做。
varchar(1) 而不是 char(1) 将在一段时间内完成,即使它需要数百万行。但是,在绝大多数情况下,时间差将是无关紧要的,因为执行此类操作的频率很低。使用varchar(1) 的动机是因为“总有一天”你可能不得不扩展它并且操作会稍微快一点是值得怀疑的。意图的明确性应该胜过过早的优化。
varchar(1) 可以存储零长度(“空”)字符串。 char(1) 不能,因为它会被填充到一个空格中。如果这种区别对您很重要,您可能会喜欢varchar。
除此之外,如果设计人员希望考虑到将来可能需要更多字符的可能性,则这种情况的一个用例可能是。
将固定长度数据类型从char(1) 更改为char(2) 意味着需要更新所有表行,并且首先删除访问该列的所有索引或约束。
在生产中对大型表进行这些更改可能是一项极其耗时的操作,需要停机。
将列从 varchar(1) 更改为 varchar(2) 要容易得多,因为它只是元数据更改(需要删除并重新创建引用该列的 FK 约束,但无需重建索引或更新数据页)。
此外,每行节省 2 个字节可能并不总是会实现。如果行定义已经很长,这并不总是会影响数据页上可以容纳的行数。另一种情况是,如果使用 Enterprise Edition 中的压缩功能,存储数据的方式无论如何都与 Mitch 的回答中提到的完全不同。 varchar(1) 和 char(1) 最终都会以相同的方式存储在短数据区域中。
@Thomas - 例如试试这个表定义。
CREATE TABLE T2
(
Code VARCHAR(1),
Foo datetime2,
Bar int,
Filler CHAR(4000),
PRIMARY KEY CLUSTERED (Code, Foo, Bar)
)
INSERT INTO T2
SELECT TOP 100000 'A',
GETDATE(),
ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
NULL
FROM master..spt_values v1, master..spt_values v2
CREATE NONCLUSTERED INDEX IX_T2_Foo ON T2(Foo) INCLUDE (Filler);
CREATE NONCLUSTERED INDEX IX_T2_Bar ON T2(Bar) INCLUDE (Filler);
对于varchar,只需将列定义从varchar(1) 更改为varchar(2)。这只是元数据更改。
ALTER TABLE T2 ALTER COLUMN Code VARCHAR(2) NOT NULL
如果从 char(1) 更改为 char(2),则必须执行以下步骤。
Code 现在存储为 char(2)。【讨论】: