将计算持久列添加到大 table

Adding computed persisted column to large table

我需要将计算的持久化列添加到一个大 table(~1B 行)。我能否以某种方式分批或使用现有的预先计算的列来执行此操作?

我首先尝试使用简单的列:

ALTER TABLE [dbo].[T] ADD [X] [decimal](32, 6) NULL
GO
UPDATE [dbo].[T]
SET [X] = [Y] / [Z]

大约 14 小时后,事务日志完全填满了 2 个磁盘,但失败了。 所以我在一个周期内分批进行了这个更新 - 全部在 7 小时内完成并且没有阻止用户查询。

现在我需要为新记录自动维护此列 - 因此考虑持久化计算列。我希望 table 的停机时间尽可能短(最好是 none)。查看简单更新和批量更新的经验,我想以某种方式批量执行此操作或使用现有列(保存计算结果)- 有什么方法可以实现吗?

我需要持久化列,因为我需要在之后对其进行索引,还因为我正在优化查询,其中计算标量(我要持久化的确切表达式)需要花费大量时间。 我也在考虑索引视图,但我担心长 运行 事务可能会出现同样的问题。

Sql 服务器 2016(企业)。简单恢复模式。

编辑: 为了我将来的参考(如果有人觉得这有帮助) - 以下是我考虑过(并测试过)的选项:

ALTER TABLE [dbo].[T] ADD [X] AZ [Y] / [Z] PERSISTED

优点:简单,保证完整性

缺点:单个事务 - 巨大的事务日志要求,如果中途失败 - 所有进度都将丢失;无法在线完成 - 针对 table 的任何查询都被锁定

优点:底层 table 在添加列时不会碎片化

缺点:主要是需要索引的唯一性。加单笔交易

优点:快速,没有 table 碎片(因为没有执行物理更改)

缺点:每次选择计算列时仍然需要对其进行计算

优点:我们可以先批量更新数据,然后让DB负责更新新插入的行。列可以在过滤索引的 WHERE 子句中使用

缺点:确保完整性是我们的责任(在我们批量更新 table 的同时可能会有一些插入/更新)

优点:与触发器相同。计算列通常比触发器更高效;我们可以以某种方式计划数据移动,以便我们得到很好的碎片整理 table

缺点:与触发器相同。另外我们需要额外的 space.

编辑2: 移动数据构建索引 2 天后,我发现计算列(即使持久化)不能用于过滤索引的 where 子句。即使在我将它从过滤器表达式移动到包含列(这样 SQL 仍然可以仅基于该索引执行选择)之后,性能也极度下降。所以我需要转换为插入触发器解决方案。

SQL 服务器允许在计算列上创建索引,即使该列本身不是持久化的。您的计算列公式似乎是确定性的,那么您是否尝试过简单地创建所需的索引?

You can define indexes on computed columns as long as the following requirements are met:

  • Ownership requirements
  • Determinism requirements
  • Precision requirements
  • Data type requirements
  • SET option requirements

https://docs.microsoft.com/en-us/sql/relational-databases/indexes/indexes-on-computed-columns?view=sql-server-2017

您可能会考虑的一件事是创建一个新的 table,其中包含定义中的计算持久列。然后,您可以从现有的 table 分批填充这个新的 table。这将最大限度地减少停机时间和阻塞。类似于您已经执行的批处理过程,但最终您将获得数据的第二个副本。完成后,您将删除原来的 table 并重命名新的。您可能需要考虑从头开始添加索引。