varchar(max) 无处不在?

Posted

技术标签:

【中文标题】varchar(max) 无处不在?【英文标题】:varchar(max) everywhere? 【发布时间】:2016-12-04 11:18:38 【问题描述】:

将所有 Sql Server 2008 字符串列设为 varchar(max) 是否有任何问题?我允许的字符串大小由应用程序管理。数据库应该只保留我给它的东西。在 Sql Server 2008 中将所有字符串列声明为 varchar(max) 类型是否会影响性能,无论实际进入它们的数据大小如何?

【问题讨论】:

在我的阅读中,这听起来像是 Sql Server varchar 列本身“自动调整大小”。那么任何给定值的最大长度为 20 的 varchar(max) 列是否与 varchar(20) 列相同? 【参考方案1】:

通过使用VARCHAR(MAX),您基本上是在告诉 SQL Server“将值存储在此字段中的最佳方式”,然后 SQL Server 将选择是将值存储为常规 VARCHAR 还是 LOB(大对象) . 一般情况下,如果存储的值小于 8,000 字节,SQL Server 会将值视为常规 VARCHAR 类型。

如果存储的值太大,则允许列从页面溢出到 LOB 页面,就像它们对其他 LOB 类型(textntextimage)所做的那样 - 如果发生这种情况那么需要额外的页面读取来读取存储在额外页面中的数据(即有一个性能),但是只有在存储的值太大时才会发生这种情况

事实上,在 SQL Server 2008 或更高版本下,即使是固定长度的数据类型(例如VARCHAR(3,000)),数据也会溢出到其他页面,但是这些页面称为行溢出数据页面,处理方式略有不同。

短版:从存储的角度来看,对于某些N,使用VARCHAR(MAX) 而不是VARCHAR(N) 并没有缺点。

(请注意,这也适用于其他可变长度字段类型NVARCHARVARBINARY

仅供参考 - 你can't create indexes on VARCHAR(MAX) columns

【讨论】:

这可能只适用于可为空的字段。每个非空 varchar(max) 或 nvarchar(max) 列需要 24 字节的额外固定分配。 docs.microsoft.com/en-us/sql/t-sql/data-types/…【参考方案2】:

索引不能超过 900 字节宽。因此,您可能永远无法创建索引。如果您的数据小于 900 字节,请使用 varchar(900)。

这是一个缺点:因为它给出了

搜索性能真的很差 没有唯一约束

【讨论】:

但是如果 varchar(max) 列没有任何大于 900 字节的值怎么办?那它会索引吗?我很困惑,因为我正在阅读的很多内容都使 varchar 列类型听起来像是在输入数据时自动调整到最大值。这对我想要的来说是完美的,因为应该由应用程序来决定最大值,而不是数据库。 创建索引时会收到警告,尝试插入 > 900 时会出错。但如果您的数据始终 我不知道我的数据是否总是 索引长文本列多久有用一次?甚至值得索引 varchar(200) 列之类的东西吗?毕竟,索引本身效率低下。搜索长“精确匹配”的需要似乎不太可能。只有知道模式的开始,模式搜索才会受益。【参考方案3】:

Simon Sabin 不久前曾就此写过一篇文章。我现在没时间去抓,但是你应该搜索一下,因为他得出的结论是你不应该默认使用 varchar(max)。

已编辑:Simon 有几篇关于 varchar(max) 的帖子。下面的 cmets 中的链接很好地展示了这一点。我认为最重要的是http://sqlblogcasts.com/blogs/simons/archive/2009/07/11/String-concatenation-with-max-types-stops-plan-caching.aspx,它谈到了 varchar(max) 对计划缓存的影响。一般原则是要小心。如果你不需要它是最大的,那么就不要使用最大 - 如果你需要超过 8000 个字符,那么当然......去吧。

【讨论】:

这个? sqlblogcasts.com/blogs/simons/archive/2005/11/18/… 不,这个:sqlblogcasts.com/blogs/simons/archive/2006/02/28/… 第一个。通过 OMG 小马。 对不起,我没有时间找到实际的链接,我正要参加会议,当时我想启动答案。 更重要的是,这个:sqlblogcasts.com/blogs/simons/archive/2009/07/11/…【参考方案4】:

对于这个问题,特别是我没有提到的几点。

    在 2005/2008/2008 R2 上,如果索引中包含 LOB 列,这将阻止在线索引重建。 2012 年取消了在线索引重建限制,但 LOB 列无法参与新功能 Adding NOT NULL Columns as an Online Operation。 可以在包含此数据类型的列的行上延长锁定时间。 (more)

我对@9​​87654323@ 的回答中涵盖了其他几个原因。

    您的查询最终可能会请求大量的内存授予,但数据大小并不合理。 在带有触发器的表上,它可以防止未添加版本控制标签的优化。

【讨论】:

【参考方案5】:

我之前问过类似的问题。得到了一些有趣的答复。看看here 有一个网站有人谈论使用宽列的不利之处,但是如果您的数据在应用程序中受到限制,我的测试证明了这一点。 您不能在列上创建索引的事实意味着我不会一直使用它们(我个人根本不会使用它们那么多,但在这方面我有点纯粹)。 但是,如果您知道它们中存储的东西不多,我认为它们并没有那么糟糕。 如果您对其中包含 varchar(max) 的记录集(或任何宽列是 char 或 varchar)的列进行任何排序,那么您可能会遭受性能损失。这些可以通过索引来解决(如果需要),但您不能将索引放在 varchar(max) 上。 如果您想将来证明您的专栏,为什么不把它们放在合理的地方。例如,名称列是 255 个字符,而不是 max... 那种东西。

【讨论】:

【参考方案6】:

还有另一个原因是要避免在所有列上使用 varchar(max)。出于同样的原因,我们使用检查约束(以避免用错误的软件或用户条目导致的垃圾填充表),我们希望防止任何添加比预期更多的数据的错误进程。例如,如果某人或某物试图将 3,000 字节添加到 City 字段中,我们会确定有问题,并希望停止进程死在其轨道上,以便尽早对其进行调试。我们还会知道 3000 字节的城市名称不可能是有效的,并且如果我们尝试使用它会弄乱报告等。

【讨论】:

【参考方案7】:

理想情况下,您应该只允许您需要的内容。这意味着如果您确定特定列(例如用户名列)的长度永远不会超过 20 个字符,则使用 VARCHAR(20) 与 VARCHAR(MAX) 可以让数据库优化查询和数据结构。

来自 MSDN: http://msdn.microsoft.com/en-us/library/ms176089.aspx

Variable-length, non-Unicode character data. n can be a value from 1 through 8,000. max indicates that the maximum storage size is 2^31-1 bytes.

对于这些列,您真的会接近 2^31-1 字节吗?

【讨论】:

我认为将其描述为“分配”并不准确。数据库当然不会在任何地方实际为您保留 2^31-1 个字节。 “用户名列 [..] 的长度永远不会超过 20 个字符” - 很好,直到有一天客户决定需要更长的时间。我们都去过那里。 :)

以上是关于varchar(max) 无处不在?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 中 varchar(max) 的等价物是啥?

RODBC sqlQuery() 在应该返回 varchar(MAX) 时返回 varchar(255)

将 UTF-8 varbinary(max) 转换为 varchar(max)

Amazon Redshift - 表列声明为 varchar(max) 但强制为 varchar(255)

使用 varchar(max) 进行慢速“选择”查询

SQL Server Always Encrypted:操作数类型冲突:varchar 与 varchar(max) 不兼容