有效改变100GB表的方法

Bry*_*ner 7 bulkinsert sql-server-2000

我们有许多数据库,在其中一个表中存储10到100千兆字节的数据.它包含图像数据.问题是很多这些数据库都是不正确地创建的.基本上,主键实际上不是主键.在可空列上使用唯一索引创建.其中一些有一个int作为主键而不是bigint.

所以我们一直在慢慢地修复这些数据库.它们通过SQL Server 2008在SQL Server 2000上运行,尽管大多数具有主键问题的都在SQL Server 2000上.问题是,我们不希望在转换表时将数据库锁定一整天.我们经历了几个策略:

  1. 告诉SQL Server直接更改列类型.这会锁定桌子直到它完成,并且在很多情况下将它放置一夜之后,它仍然没有完成.

  2. 一次性将所有图像插入新表中.这更容易被中断,但整个表基本上都被写入了进程中的日志文件.

  3. 在目标表中不存在行的时间插入100行.好处是,他们可以继续使用数据库进行此操作(性能大幅提升),并且可以在任何时候任意停止和重新启动,并且它可以防止100GB +日志文件.这就是我们目前正在做的事情,但是随着目标表变得越来越大,找到不存在的前100行变得非常慢.UPDATE STATISTICS和DBCC INDEXDEFRAG有很大的帮助,但在最近的尝试中,我们达到了这一点,一次只有100张图像坐在那里没有响应.

    INSERT INTO %s  
      SELECT TOP 100 Source.*  
      FROM %s AS Source WITH (NOLOCK)  
      LEFT OUTER JOIN %s AS Target WITH (NOLOCK) ON Source.DocumentID = Target.DocumentID  
      WHERE Target.DocumentID IS NULL  
      ORDER BY Source.DocumentID  
    
    Run Code Online (Sandbox Code Playgroud)

所以问题是,是否有可以以高效和可恢复的方式复制批量数据的选项?它不必100%准确,我们总是可以返回并修复最后的任何差异,只要它完成99%的工作.

Bra*_*adC 3

连接是问题所在。不要那样做。只需使用当前的聚集索引,以合理的间隔循环遍历当前表。就像是:

Declare @idrange int;
Set @idrange = 1;

WHILE @idrange < 10000000

INSERT INTO Destination
  SELECT *  
  FROM Source
  WHERE DocumentID between @idrange and @idrange + 999
  ORDER BY Source.DocumentID  

Set @idrange = @idrange + 1000
End 
Run Code Online (Sandbox Code Playgroud)

请注意,为了获得最佳速度,请从目标表中删除所有索引(包括聚集索引),然后在插入所有行后添加索引。

编辑:更改范围间隔以防止重叠(因为 BETWEEN 包括端点)

最后澄清一下:我的示例脚本的总体要点是,您只想以某种合理的顺序遍历当前记录,并将它们全部批量放入新表中。没有理由每次都继续检查目标表,因为您应该已经知道放在那里的内容以及还剩下的内容。大多数时候,使用聚集索引(如果有)是有意义的,因为这意味着它可以遍历表的物理顺序,而无需进行书签查找。如果表没有簇,那么就使用最有意义的东西(可能是你的 PK)。