在 SQL Server 2008 的大表中将 VARCHAR 更改为 NVARCHAR 字段的有效方法?

ElH*_*aix 7 performance database-design varchar sql-server-2008-r2 type-conversion

我知道在向大表中添加新字段时,建议将它们添加到字段的末尾而不是中间的某个位置,并且想知道在更改字段类型时是否适用这样的事情?

我有一个包含大约一百万条记录的表,其中包含几个 VARCHAR 类型的字段。我想将这些更改为 NVARCHAR,但据我所知,这将需要一些时间和资源,因为字段位于表的中间,并且 SQL Server 必须进行大量复制/重新排序。

实现这一目标的有效方法是什么?

Aar*_*and 20

一种方法可能是:

  1. 添加 NULLable NVARCHAR 列
  2. 使用批处理,一次更新多行(例如 1000 或 10000 行)
  3. 备份日志,检查点,批次之间有什么
  4. 更新所有行后,删除旧列并重命名新列
  5. 重建索引

从长远来看,这不会更快,并且仍然需要一个维护窗口(因为您不希望用户更新您已经更新的行,除非您放置一个临时触发器来应对),但它会阻止大笔交易,经过几次更新后,您将更可预测需要多长时间。

您可以通过创建一个新表并在完成后重命名来做同样的事情......虽然这避免了第 5 步的需要,但它会导致更多的数据流失,并且由于约束、外键、触发器而可能会出现更多问题等可能与表格有关的内容。

  • 使用批次的奖励积分。太多的人已经忘记了这种古老而有效的技术。 (2认同)

Fab*_*ujo 7

直接回答问题,执行操作有两种方式。

  • 如果表上涉及的varchar列数很少(一两个),创建伪临时列更实用
  • 如果 varchar 列的数量更大,上面的方法就不太实用了——所以你创建了一个伪表。这最常用于某些数据库工具(如 ErWin 或 ER/Studio)的元数据更新脚本(我使用了这两个工具,并在应用前查看了生成的脚本)

关于大表的注意事项:如果表有几千条或更少的记录,您可以立即进行操作。在百万记录表的情况下,批量执行更实用(假设每次有 1000 条或 100 条记录)。

伪临时列

伪临时列(我忘记是否有另一个更合适的名称)是用于存储转换结果的列。在这种情况下,它们也将是该过程之后的最终列。

  1. 创建具有预期长度的新列。不要忘记在新定义中包含任何检查约束或默认值
  2. 执行更新(或更新,参见上面的观察)以将旧列的数据存储在新列中。
  3. 执行日志备份并执行检查点,以免日志变得异常大。
  4. 如果旧列有任何与之关联的约束,则删除它们。
  5. 删除旧列。
  6. 将新列重命名为旧列名
  7. 重建受影响的索引(或全部重建,如果受影响的列也是聚集主键约束的一部分——很少有人使用 (n)varchar 作为 PK,但我见过一些)。

这与Aaron 的回答中详述的过程相同。

伪临时表

当修改在多个列中时,基于旧表的架构创建一个新表更实用。

  1. 创建一个新表,没有任何表约束(PK、FK 等)。此时只带列(NOT NULL、DEFAULT、CHECK 等)
  2. 将旧表中的数据插入到新表中(请参阅上面关于大表注释)。SET IDENTITY_INSERT 这里是必须的。
  3. 现在,删除旧表上的所有表约束(PK、FK、检查)和触发器。在新表上重新创建约束和触发器。
  4. 在新表上重新创建旧表的所有其他索引(一次全部或一次一个,取决于您的维护时段)。除非表没有聚集索引,否则这必须在第 3 步之后完成。或者,至少,在创建 PK 约束之后。
  5. 检查一切是否正常(如果您没有忘记过程中的触发器或约束),如果一切正常,则删除旧表。
  6. 将新表重命名为旧表上的名称

第 4 步注意事项:如果您检测到任何重复索引(检测重复索引是一个很长的主题,请参阅 SQLSkills.com 上的 Kimberly Tripp 博客),如果是这种情况,您就有机会摆脱它们。

性能影响

从 VARCHAR 更改为 NVARCHAR 对性能有一些影响,至少对于低于 2008R2 的任何 SQL Server。对于 SQL 2008 R2,Aaron Bertrand 有一些关于 Unicode 压缩功能的博客文章 - 当使用 NVarchar 列存储可以存储在 VARCHAR 列上的内容时,这可以抵消平衡。我没有像文章应得的那样完整地阅读它们,但是这个主题很有趣。

NVARCHAR 列通常(IOW,2008R2 之前)将所有字符存储在每个字符 2 个字节的列中。例如,字符串“MSSQL”将在 VARCHAR 列上以 5 个字节存储,在 NVARCHAR 列上以 10 个字节存储。由于非 LOB 字符串列被限制为最多存储 8000个字节,这意味着 VARCHAR 可以存储 8000 个字符,而 NVARCHR 限制为 4000 个。

这些事实的含义:

  • 由于索引键限制为 900 字节(请参阅 CREATE INDEX 上的文档),如果您尝试索引 NVARCHAR(500) 列,则该命令不会失败(如果这是索引键上唯一的一列),但如果您 UPDATE 或INSERT 一行超过 450 -(索引键上其他列的总大小,如果是这种情况)字符操作将失败。
  • 要操作的字节越多,要做的工作就越多。您读/写/比较/缓存双字节。
  • 根据表的大小、字符串列对表存储大小的影响以及表对数据库大小的参与程度,您可以预期(已使用的)数据库大小及其影响的所有变量都会增加直接与否(如备份/恢复时间、索引维护等)。

编辑:正如 gbn 所说,当您有明确的要求需要填充 NVARCHAR 列时,不值得仅仅为了使用 VARCHAR 而创建一些东西。