【问题标题】:Should I use nvarchar(max) in place of a nvarchar(64) column or as an additional column?我应该使用 nvarchar(max) 代替 nvarchar(64) 列还是作为附加列?
【发布时间】:2023-03-20 02:31:02
【问题描述】:

我正在构建一个表来跟踪我的数据库中特定对象的历史记录。 目前我有以下列:

HistoryId int IDENTITY(1,1) NOT NULL
HistoryDate datetimeoffset(7) NOT NULL 
HistoryTypeId int NOT NULL
HistoryDetails nvarchar(max) NULL

在大多数情况下,每个历史记录项都可以通过 HistoryTypeId 进行自我解释,因此 HistoryDe​​tails 要么为 Null,要么非常小。但是对于几个历史类型,细节数据会很大。是否可以使用 nvarchar(max) 处理所有记录,或者我应该将它分开并为需要超过 64 个字符的历史类型添加一个额外的列(见下文)?粗略估计,80%-90% 的记录不需要超过 64 个字符的详细信息,表中将有数百万条记录。

HistoryId int IDENTITY(1,1) NOT NULL
HistoryDate datetimeoffset(7) NOT NULL 
HistoryTypeId int NOT NULL
HistoryDetails nvarchar(64) NULL
HistoryDetailsMore nvarchar(max) NULL

【问题讨论】:

  • 能否请您针对您的表发布一个典型的查询?
  • Quassnoi:您的计算列解决方案正是我想要的。我的典型查询是这样的: SELECT UserId, HistoryDate, HistoryTypeId, HistoryDe​​tails FROM History WHERE UserId=XXX 所以我想要所有 HistoryTypeIds 的 HistoryDe​​tails 但大多数情况下只有前 64 个字符(否则它将通过辅助查询处理)。

标签: sql sql-server tsql database-design data-modeling


【解决方案1】:

您不能将 NVARCHAR(MAX) 作为普通 B-Tree 索引中键的一部分(您仍然可以将其用作索引中的包含列)。

否则,只要列中的数据不超过行大小阈值,存储就会相同。

由于您可能无论如何都不打算为该字段编制索引,因此最好将其创建为 NVARCHAR(MAX)。

即使您仍想为其编制索引(例如,使用LIKE 进行前缀搜索),您也可以创建一个计算的NVARCHAR(450) 列,在该列上创建一个索引,并将其添加到您的查询中以进行粗略过滤.

有关详细信息,请参阅我的博客中的此条目:

如果您打算只对小列进行精确搜索,请创建一个计算列,对其进行索引并像这样查询:

ALTER TABLE History ADD HistoryDetailsIndex AS SUBSTRING(HistoryDetails, 1, 50)

CREATE INDEX ix_mytable_typeid_details ON History (HistoryTypeId, HistoryDetailsIndex) INCLUDE (HistoryDetails)

SELECT  COUNT(*)
FROM    History
WHERE   HistoryTypeId = 123
        AND HistoryDetailsIndex LIKE 'string_prefix_up_to_50_characters%'
        AND HistoryDetails = 'string_prefix_up_to_50_characters_plus_everything_after_it'

这将仅将您的HistoryDetails 中的第一个50 字符包含到索引键中(将在LIKE 条件中搜索),并将所有内容都包含在包含的列中。

如果您绝对确定您永远不会搜索长度超过 50 个字符的字符串,您可以省略包含的列并直接使用:

SELECT  COUNT(*)
FROM    History
WHERE   HistoryTypeId = 123
        AND HistoryDetailsIndex = 'string_prefix_up_to_50_characters'

这将使索引更短。

但是,如果您提供的字符串长度超过 50 个字符,这将失败,因此如果您绝对确定永远不会搜索长字符串,请使用它。

【讨论】:

  • 如果它超过了那些需要 nvarchar(max) 的项目的行大小阈值怎么办?这有什么改变吗?
  • 在内部,此列将存储在行外 (msdn.microsoft.com/en-us/library/ms186981.aspx)。从用户的角度来看,什么都不会改变:您照常使用该列。
  • 因此,从您所说的(以及您的博客文章)来看,因为 80-90% 的列中值较小的记录需要被索引,但 10% 的记录大值不需要索引,我应该使用两列架构。包含在索引中的 nvarchar(64) 和非索引 nvarchar(max)。你能确认一下吗?
  • 您将使用索引做什么?如果用于前缀搜索,只需将数据保存在 NVARCHAR(MAX) 中并创建计算列 NVARCHAR(450)。这个计算列将实际存储和索引数据的第一个 450 字符,这对于任何前缀搜索通常都绰绰有余。如果您使用索引来避免key lookups / RID lookups,那么只需创建一个以NVARCHAR(MAX) 作为包含列的索引。该索引不允许对您的字符串进行前缀查找,但如果您仅使用索引在查询中覆盖的列,它仍会从索引中获取所有数据。
  • 我会使用这样的索引:SELECT COUNT(*) FROM History WHERE HistoryTypeId=123 AND HistoryDe​​tails='XYZ' 但仅适用于 80-90% 具有较小值的记录那个专栏。
【解决方案2】:

首先要知道 varchar(MAX) 最多可以存储 2gb 的空间,在后台它实际上使用 TEXT 值,随后它使用的处理比 varchar(8000) 更多或更少。

如果您在 varchar(max) 中存储大量较小的数据,它将被视为普通的 varchar 列,除非您超过 8000,否则它将被视为 varchar(max)。

该列是否已编入索引,还是您要编入索引?如果是这样,请避开 varchar(max)。

我只会选择一个更高的值,比如 varchar(255) 并强制用户适应您的数据库设计,而不是相反。

【讨论】:

  • 您对索引的评论与 Quassnoi 的回答冲突。我将为该列编制索引,但这只是因为该列中包含少量数据的项目类型(80-90% 的记录)。
【解决方案3】:

由于您使用的是 nvarchar,因此您很可能已经支付了可变长度记录的开销,除非 SQLServer 在小情况下覆盖可变长度。但是,对于 nvarchar(64) 和 nvarchar(max) 之间的短记录,磁盘上的空间不应该改变。他们应该只占用适合其数据所需的空间。通常,该数字仅用于约束数据。如果您不想限制它,那么您不应该在使用您尚未支付的那两个之间支付罚款。

【讨论】:

  • 所以你是说只使用单个 HistoryDe​​tails nvarchar(max) 列?
  • 我想,除非您对该列进行索引或定期编辑这些字段,否则这将是比创建单独的表更好的解决方案,是的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
  • 1970-01-01
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多