[英]Fastest way to update 120 Million records
我需要在一個1.2億記錄表中初始化一個值為-1的新字段。
Update table
set int_field = -1;
我讓它運行了5個小時才取消它。
我嘗試運行它,事務級別設置為讀取未提交的相同結果。
Recovery Model = Simple.
MS SQL Server 2005
如何更快地完成這項工作?
更新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很容易搞砸。
如果您有磁盤空間,則可以使用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
我將任務分解為更小的單位。 為您的表測試不同的批處理大小間隔,直到找到最佳執行的間隔。 這是我過去使用過的一個示例。
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已編制索引,請在運行更新之前刪除索引。 然后再次創建索引......
對於1.2億人來說,5個小時似乎很多。
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
set rowcount 1000000
Update table set int_field = -1 where int_field<>-1
看看需要多快,調整和重復
我先嘗試的是
在更新之前首先刪除所有約束,索引,觸發器和全文索引。
如果上面的表現不夠好,我的下一步行動就是
創建一個包含1200萬條記錄的CSV文件,並使用bcp批量導入它。
最后,我創建了一個新的堆表(意思是沒有主鍵的表),在不同的文件組上沒有索引,用-1填充它。 對舊表進行分區,並使用“switch”添加新分區。
添加新列(“初始化新字段”)並為每個現有行設置單個值時,我使用以下策略:
ALTER TABLE MyTable
add NewColumn int not null
constraint MyTable_TemporaryDefault
default -1
ALTER TABLE MyTable
drop constraint MyTable_TemporaryDefault
如果列可以為空並且您不包含“聲明”約束,則對於所有行,該列將設置為null。
聽起來像索引問題,就像Pabla Santa Cruz提到的那樣。 由於您的更新不是條件更新,因此您可以刪除列並使用DEFAULT值重新添加它。
一般來說,建議如下:
但在特殊情況下,您應該選擇最合適的解決方案或它們的組合。
還要記住,某些時候索引可能很有用,例如,當您通過某些條件執行非索引列的更新時。
如果表有一個可以迭代的索引,我會將update top(10000)
語句放在while循環中移動數據。 這將使事務日志保持苗條,並且不會對磁盤系統產生如此巨大的影響。 另外,我建議使用maxdop
選項(將其設置為接近1)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.