最快的方法来更新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值重新添加。
一般来说,推荐是下一个:
- 删除或只是禁用表上的所有指标,触发器,约束条件;
- 更频繁地执行COMMIT(例如每更新1000条logging之后);
- 使用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