将计算的持久化列添加到大型表

时间:2018-09-26 19:19:02

标签: sql-server indexing large-data calculated-columns

我需要将计算的持久化列添加到大型表(〜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仍可以仅基于此索引执行选择)之后,性能也大大降低了。所以我需要转换以插入触发器解决方案。

2 个答案:

答案 0 :(得分:1)

SQL Server允许在计算列上创建索引,即使该列本身未持久保存也是如此。您计算出的列公式似乎具有确定性,那么您是否尝试过简单地创建所需的索引?

  

您可以在计算列上定义索引,只要满足以下条件   满足要求:

     
      
  • 所有权要求
  •   
  • 确定性要求
  •   
  • 精度要求
  •   
  • 数据类型要求
  •   
  • SET选项要求
  •   

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

答案 1 :(得分:0)

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