最快的方法来更新1.2亿条logging

我需要在一个1.2亿logging表中初始化值为-1的新字段。

更新表set int_field = -1;

在取消之前,我让它运行5个小时。

我试着用事务级别来运行它,以读取未提交的结果。

恢复模式=简单。

MS SQL Server 2005

任何关于更快完成这个任务的build议?

更新120Mlogging表的唯一方法是使用填充第二个表的SELECT语句。 这样做时你必须小心。 下面的说明。


简单案例

对于无聚簇索引的表,在无并发DML的时间内:

  • SELECT *, new_col = 1 INTO clone.BaseTable FROM dbo.BaseTable
  • 在新表上重新创build索引,约束等
  • 切换新旧W / ALTER SCHEMA … TRANSFER。
  • 放旧桌子

如果您无法创build克隆模式,则同一模式中的另一个表名将会执行。 切记在切换后重新命名所有约束和触发器(如果适用)。


非简单案例

首先,在不同的模式下重新创buildBaseTable ,例如clone.BaseTable 。 使用单独的架构将稍后简化重命​​名过程。

  • 包括聚集索引 (如果适用)。 请记住,主键和唯一约束可能会聚集在一起,但并不一定如此。
  • 包括标识列和计算列 (如果适用)。
  • 包括你的新的INT列 ,无论它属于哪里。
  • 请勿包含以下任何内容:
    • 触发器
    • 外键约束
    • 非聚簇索引/主键/唯一约束
    • 检查约束或默认约束。 默认值没有太大区别,但是我们试图保持最小的状态。

然后,testing你的插入w / 1000行:

 -- assuming an IDENTITY column in BaseTable SET IDENTITY_INSERT clone.BaseTable ON GO INSERT clone.BaseTable WITH (TABLOCK) (Col1, Col2, Col3) SELECT TOP 1000 Col1, Col2, Col3 = -1 FROM dbo.BaseTable GO SET IDENTITY_INSERT clone.BaseTable OFF 

检查结果。 如果一切都按顺序出现:

  • 截断克隆表
  • 确保数据库处于批量logging或简单恢复模式
  • 执行完整的插入。

这将需要一段时间,但不会更新。 一旦完成,检查克隆表中的数据,以确保一切正确。

然后,重新创build所有非集群主键/唯一约束/索引和外键约束(按此顺序)。 重新创build默认和检查约束(如果适用)。 重新创build所有触发器。 在单独的批次中重新创build每个约束,索引或触发器。 例如:

 ALTER TABLE clone.BaseTable ADD CONSTRAINT UQ_BaseTable UNIQUE (Col2) GO -- next constraint/index/trigger definition here 

最后,将dbo.BaseTable移动到备份模式,并将clone.BaseTable到dbo模式(或者您的表所在的位置)。

 -- -- perform first true-up operation here, if necessary -- EXEC clone.BaseTable_TrueUp -- GO -- -- create a backup schema, if necessary -- CREATE SCHEMA backup_20100914 -- GO BEGIN TRY BEGIN TRANSACTION ALTER SCHEMA backup_20100914 TRANSFER dbo.BaseTable -- -- perform second true-up operation here, if necessary -- EXEC clone.BaseTable_TrueUp ALTER SCHEMA dbo TRANSFER clone.BaseTable COMMIT TRANSACTION END TRY BEGIN CATCH SELECT ERROR_MESSAGE() -- add more info here if necessary ROLLBACK TRANSACTION END CATCH GO 

如果您需要释放磁盘空间,那么您现在可以放弃原来的表格,但谨慎的做法是保持一段时间。

不用说,这是理想的离线操作。 如果有人在执行此操作时修改数据,则必须使用模式切换执行修正操作。 我build议在dbo.BaseTable上创build一个触发器,将所有DMLlogging到一个单独的表中。 在开始插入之前启用此触发器。 然后在执行模式转移的同一个事务中,使用日志表执行修改。 先testing一下数据的一个子集! 三angular洲很容易搞砸了。

如果您有磁盘空间,则可以使用SELECT INTO并创build一个新表。 这是最低限度的logging,所以它会更快

 select t.*, int_field = CAST(-1 as int) into mytable_new from mytable t -- create your indexes and constraints GO exec sp_rename mytable, mytable_old exec sp_rename mytable_new, mytable drop table mytable_old 

我把任务分解成更小的单位。 testing不同的批量大小间隔为您的表,直到您find一个间隔,执行最佳。 这是我以前用过的一个样本。

 declare @counter int declare @numOfRecords int declare @batchsize int set @numOfRecords = (SELECT COUNT(*) AS NumberOfRecords FROM <TABLE> with(nolock)) set @counter = 0 set @batchsize = 2500 set rowcount @batchsize while @counter < (@numOfRecords/@batchsize) +1 begin set @counter = @counter + 1 Update table set int_field = -1 where int_field <> -1; end set rowcount 0 

如果您的int_field已编入索引,请在运行更新之前删除索引。 然后再次创build您的索引…

5个小时对于1.2亿个recs来说似乎很多。

 set rowcount 1000000 Update table set int_field = -1 where int_field<>-1 

看看有多快,需要调整和重复

我想先尝试的是
在更新之前首先删除所有约束,索引,触发器和全文索引。

如果上面没有足够的性能,我的下一步将是
创build一个包含1200万条logging的CSV文件,并使用bcp批量导入它。

最后,我会创build一个新的堆表(不含主键的表),在不同的文件组上没有索引,用-1填充它。 对旧表分区,并使用“switch”添加新分区。

添加新列(“初始化新字段”)并为每个现有行设置单个值时,我使用以下策略:

 ALTER TABLE MyTable add NewColumn int not null constraint MyTable_TemporaryDefault default -1 ALTER TABLE MyTable drop constraint MyTable_TemporaryDefault 

如果该列可以为空,并且不包含“声明”约束,那么对于所有行,该列将设置为空。

听起来像索引问题,就像Pabla Santa Cruz提到的那样。 由于您的更新不是有条件的,您可以删除列并使用DEFAULT值重新添加。

一般来说,推荐是下一个:

  1. 删除或只是禁用表上的所有指标,触发器,约束条件;
  2. 更频繁地执行COMMIT(例如每更新1000条logging之后);
  3. 使用select … into。

但在特殊情况下,您应该select最合适的解决scheme或其组合。

另外请记住,某些时候索引可能是有用的,例如,当您通过某些条件执行非索引列的更新时。

如果表中有一个你可以迭代的索引,我会把update top(10000)语句放在while循环中移动数据。 这样可以保持事务日志的轻薄,并且不会对磁盘​​系统产生如此巨大的影响。 此外,我会build议玩maxdop选项(设置它接近1)。

 declare @cnt bigint set @cnt = 1 while @cnt*100<10000000 begin UPDATE top(100) [Imp].[dbo].[tablename] SET [col1] = xxxx WHERE[col2] is null print '@cnt: '+convert(varchar,@cnt) set @cnt=@cnt+1 end