我需要将计算的持久化列添加到大型表(〜1B行)中。我可以批量或使用现有的预先计算的列来执行此操作吗?
我首先尝试使用简单的列:
ALTER TABLE [dbo].[T] ADD [X] [decimal](32, 6) NULL
GO
UPDATE [dbo].[T]
SET [X] = [Y] / [Z]
大约14个小时后,此磁盘将事务日志完全填满了2个磁盘,但失败了。 因此,我分批进行了一次此更新-所有更新均在7个小时内完成,并且没有阻止用户查询。
现在,我需要为新记录自动维护此列-因此考虑持久化计算列。我希望表的停机时间尽可能小(理想情况下没有)。查看简单更新和批量更新的经验,我想以某种方式批量进行此操作,或使用现有的列(保存计算结果)-有什么方法可以实现这一目标?
我需要持久化列,因为之后需要对其进行索引,而且还因为我正在优化查询(计算的标量(我要持久化的确切表达式))花费大量时间。 我也在考虑索引视图,但是我担心长时间运行的事务可能会出现相同的问题。
Sql Server 2016(企业)。简单的恢复模式。
编辑: 供我将来参考(如果有人认为有帮助)-我考虑(并测试)了以下选项:
ALTER TABLE [dbo].[T] ADD [X] AZ [Y] / [Z] PERSISTED
优点:简单,确保完整性
缺点:单个事务-巨大的事务日志要求,如果中途失败-所有进度都会丢失;无法在线完成-对表的任何查询均已锁定索引视图
创建视图[dbo]。[T_view] -索引视图 与模式绑定 如 选择 [Y], [Z], [Y] /(NULLIF(Z,0))AS [Z] 从 [dbo]。[T] 开始
-实例化视图的第一个索引必须是唯一的并且已聚类
创建唯一的聚集索引IDX_T_view
开启[dbo]。[T_view]([Z])
开始
优点:在添加列时基础表不会碎片化
缺点:主要是索引的唯一性需求。再加上一次交易
具有索引的非持久计算列:
ALTER TABLE [dbo]。[T]添加[X] AZ [Y] / [Z]
在[dbo]上创建命名索引[IX_T]。[T] ( [X] ASC )
优点:快速,没有表碎片(因为没有执行物理更改)
缺点:每次选择计算列时仍需要计算
优点:我们可以先批量更新数据,然后让数据库负责更新新插入的行。列可以在过滤索引的WHERE子句中使用
缺点:要确保我们的完整性(在批量更新表时,可能会在紧急状态下进行一些插入/更新)
优点:与扳机相同。计算列通常比触发器更有效;我们可以以某种方式计划数据移动,以便对表进行良好的碎片整理
缺点:与触发器相同。另外,我们需要额外的空间。
EDIT2: 移动数据构建索引两天后,我发现计算所得的列(即使已保留)也不能在过滤索引的where子句中使用。即使我将其从过滤器表达式移到包括列(以便SQL仍可以仅基于此索引执行选择)之后,性能也大大降低了。所以我需要转换以插入触发器解决方案。
答案 0 :(得分:1)
SQL Server允许在计算列上创建索引,即使该列本身未持久保存也是如此。您计算出的列公式似乎具有确定性,那么您是否尝试过简单地创建所需的索引?
您可以在计算列上定义索引,只要满足以下条件 满足要求:
- 所有权要求
- 确定性要求
- 精度要求
- 数据类型要求
- SET选项要求
答案 1 :(得分:0)
您可能要考虑的一件事是创建一个新表,并在定义中包含计算出的持久性列。然后,您可以从现有表中批量填充该新表。这样可以最大程度地减少停机时间和阻塞。与您已经完成的批处理过程类似,但是最后您将获得第二份数据副本。完成后,您将删除原始表并重命名新表。您可能要考虑从头开始添加索引。