更新1.2亿条记录的最快方式

Bob*_*bst 44 sql sql-server sql-server-2005

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

Update table
       set int_field = -1;
Run Code Online (Sandbox Code Playgroud)

我让它运行了5个小时才取消它.

我尝试运行它,事务级别设置为读取未提交的相同结果.

Recovery Model = Simple.
MS SQL Server 2005
Run Code Online (Sandbox Code Playgroud)

如何更快地完成这项工作?

Pet*_*hia 36

更新120M记录表的唯一合理方法是使用SELECT填充第二个表的语句.这样做时你必须小心.说明如下.


简单案例

对于没有聚集索引的表,在w/out并发DML的时间内:

  • SELECT *, new_col = 1 INTO clone.BaseTable FROM dbo.BaseTable
  • 在新表上重新创建索引,约束等
  • 切换旧的和新的w/ALTER SCHEMA ... TRANSFER.
  • 放下旧桌子

如果无法创建克隆架构,则同一架构中的其他表名称将起作用.切记在切换后重命名所有约束和触发器(如果适用).


非简单案例

首先,BaseTable在不同的模式下重新创建相同的名称,例如clone.BaseTable.使用单独的模式将在以后简化重命​​名过程.

  • 如果适用,请包括聚簇索引.请记住,主键和唯一约束可能是聚类的,但不一定如此.
  • 如果适用,请包括标识列和计算列.
  • 包括您的新INT列,无论它在哪里.
  • 不包括以下任何内容:
    • 触发器
    • 外键约束
    • 非聚集索引/主键/唯一约束
    • 检查约束或默认约束.默认值没有多大区别,但我们试图将事情保持在最低限度.

然后,测试你的插件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
Run Code Online (Sandbox Code Playgroud)

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

  • 截断克隆表
  • 确保数据库处于批量记录或简单恢复模型中
  • 执行完整插入.

这需要一段时间,但不会像更新那么长.完成后,检查克隆表中的数据以确保一切正确.

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

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

最后,转到dbo.BaseTable备份架构和clone.BaseTabledbo架构(或者您的表应该存在的任何位置).

-- -- 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
Run Code Online (Sandbox Code Playgroud)

如果您需要释放磁盘空间,此时可能会删除原始表,但保持一段时间可能是谨慎的.

不用说,这理想情况下是离线操作.如果您在执行此操作时有人修改数据,则必须使用架构开关执行校正操作.我建议创建一个触发器,dbo.BaseTable将所有DML记录到一个单独的表中.在开始插入之前启用此触发器.然后,在执行模式传输的同一事务中,使用日志表执行校正.首先在数据子集上进行测试!Deltas很容易搞砸.


小智 13

如果您有磁盘空间,则可以使用SELECT INTO并创建新表.它的记录最少,因此速度会快得多

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
Run Code Online (Sandbox Code Playgroud)


小智 9

我将任务分解为更小的单位.为您的表测试不同的批处理大小间隔,直到找到最佳执行的间隔.这是我过去使用过的一个示例.

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
Run Code Online (Sandbox Code Playgroud)