我需要在一个1.2亿记录表中初始化一个值为-1的新字段。
Update table
set int_field = -1;
我让它在取消之前运行了5个小时。
我尝试运行它,事务级别设置为读取未提交且具有相同的结果。
Recovery Model = Simple.
MS SQL Server 2005
有关更快完成此任务的任何建议吗?
答案 0 :(得分:34)
更新120M记录表的唯一合理方法是使用SELECT
语句填充第二个表。这样做时你必须小心。说明如下。
简单案例
对于没有聚集索引的表,在w / out并发DML的时间内:
SELECT *, new_col = 1 INTO clone.BaseTable FROM dbo.BaseTable
如果无法创建克隆架构,则同一架构中的其他表名称将起作用。切记在切换后重命名所有约束和触发器(如果适用)。
非简单案例
首先,在不同的架构下重新创建具有相同名称的BaseTable
,例如clone.BaseTable
。使用单独的模式将在以后简化重命名过程。
然后,测试你的插件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
检查结果。如果一切按顺序显示:
这需要一段时间,但不会像更新那么长。完成后,检查克隆表中的数据以确保一切正确。
然后,重新创建所有非群集主键/唯一约束/索引和外键约束(按此顺序)。如果适用,重新创建默认值并检查约束。重新创建所有触发器。在单独的批处理中重新创建每个约束,索引或触发器。例如:
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
如果您需要释放磁盘空间,此时您可能会放弃原始表格,但保持一段时间可能是谨慎的。
毋庸置疑,理想情况下,这是离线操作。如果您在执行此操作时有人修改数据,则必须使用架构开关执行校正操作。我建议在dbo.BaseTable
上创建一个触发器,将所有DML记录到一个单独的表中。在开始插入之前启用此触发器。然后,在执行模式传输的同一事务中,使用日志表执行校正。首先在数据子集上进行测试! Deltas很容易搞砸。
答案 1 :(得分: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
答案 2 :(得分: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
答案 3 :(得分:4)
如果您的int_field已编制索引,请在运行更新之前删除索引。然后再次创建索引......
对于1.2亿人来说,5个小时似乎很多。
答案 4 :(得分:3)
set rowcount 1000000
Update table set int_field = -1 where int_field<>-1
了解根据需要采取,调整和重复的速度
答案 5 :(得分:2)
我首先尝试的是什么 在更新之前首先删除所有约束,索引,触发器和全文索引。
如果上述情况不够好,我的下一步行动将是 创建一个包含1200万条记录的CSV文件,并使用bcp批量导入它。
最后,我创建了一个新的堆表(意思是没有主键的表),在不同的文件组上没有索引,用-1填充它。对旧表进行分区,并使用“switch”添加新分区。
答案 6 :(得分:2)
当添加新列(“初始化新字段”)并为每个现有行设置单个值时,我使用以下策略:
ALTER TABLE MyTable
add NewColumn int not null
constraint MyTable_TemporaryDefault
default -1
ALTER TABLE MyTable
drop constraint MyTable_TemporaryDefault
如果列可以为空并且您没有包含“声明”约束,则对于所有行,该列将设置为null。
答案 7 :(得分:2)
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
答案 8 :(得分:1)
听起来像索引问题,就像Pabla Santa Cruz提到的那样。由于您的更新不是有条件的,您可以删除列并使用DEFAULT值重新添加它。
答案 9 :(得分:0)
一般来说,建议如下:
但在特殊情况下,您应该选择最合适的解决方案或它们的组合。
还要记住,某些时候索引可能是有用的,例如当您按某种条件执行非索引列的更新时。
答案 10 :(得分:0)
如果表中有一个可以迭代的索引,我会将update top(10000)
语句放在while循环中移动数据。这将使事务日志保持苗条,并且不会对磁盘系统产生如此巨大的影响。另外,我建议使用maxdop
选项(将其设置为接近1)。