将计算持久列添加到大 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 的任何查询都被锁定
索引视图
创建视图 [dbo].[T_view]
--索引视图
与架构绑定
作为
SELECT
[Y],
[Z],
[Y] / (NULLIF(Z, 0)) 作为 [Z]
从
[dbo].[T]
去
--物化视图的第一个索引必须是唯一的和聚集的
创建唯一聚集索引 IDX_T_view
ON [dbo].[T_view] ([Z])
去
优点:底层 table 在添加列时不会碎片化
缺点:主要是需要索引的唯一性。加单笔交易
具有索引的非持久计算列:
ALTER TABLE [dbo].[T] 添加 [X] AZ [Y] / [Z]
在 [dbo].[T] 上创建非聚集索引 [IX_T]
(
[X] 升序
)
优点:快速,没有 table 碎片(因为没有执行物理更改)
缺点:每次选择计算列时仍然需要对其进行计算
- Insert/update 触发器:
优点:我们可以先批量更新数据,然后让DB负责更新新插入的行。列可以在过滤索引的 WHERE 子句中使用
缺点:确保完整性是我们的责任(在我们批量更新 table 的同时可能会有一些插入/更新)
- 新建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
您可能会考虑的一件事是创建一个新的 table,其中包含定义中的计算持久列。然后,您可以从现有的 table 分批填充这个新的 table。这将最大限度地减少停机时间和阻塞。类似于您已经执行的批处理过程,但最终您将获得数据的第二个副本。完成后,您将删除原来的 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 的任何查询都被锁定
索引视图
创建视图 [dbo].[T_view] --索引视图 与架构绑定 作为 SELECT [Y], [Z], [Y] / (NULLIF(Z, 0)) 作为 [Z] 从 [dbo].[T] 去
--物化视图的第一个索引必须是唯一的和聚集的 创建唯一聚集索引 IDX_T_view
ON [dbo].[T_view] ([Z]) 去
优点:底层 table 在添加列时不会碎片化
缺点:主要是需要索引的唯一性。加单笔交易
具有索引的非持久计算列:
ALTER TABLE [dbo].[T] 添加 [X] AZ [Y] / [Z]
在 [dbo].[T] 上创建非聚集索引 [IX_T] ( [X] 升序 )
优点:快速,没有 table 碎片(因为没有执行物理更改)
缺点:每次选择计算列时仍然需要对其进行计算
- Insert/update 触发器:
优点:我们可以先批量更新数据,然后让DB负责更新新插入的行。列可以在过滤索引的 WHERE 子句中使用
缺点:确保完整性是我们的责任(在我们批量更新 table 的同时可能会有一些插入/更新)
- 新建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
您可能会考虑的一件事是创建一个新的 table,其中包含定义中的计算持久列。然后,您可以从现有的 table 分批填充这个新的 table。这将最大限度地减少停机时间和阻塞。类似于您已经执行的批处理过程,但最终您将获得数据的第二个副本。完成后,您将删除原来的 table 并重命名新的。您可能需要考虑从头开始添加索引。