有效改变100GB表的方法

时间:2010-09-13 17:06:11

标签: sql-server-2000 bulkinsert

我们有许多数据库可以在其中一个表中存储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  
    
  4. 所以问题是,是否有可以以高效和可恢复的方式复制批量数据的选项?它不必100%准确,我们总是可以返回并修复最后的任何差异,只要它完成99%的工作。

1 个答案:

答案 0 :(得分: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 

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

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

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