2016-06-09 49 views
2

我想自今年年初標誌列,對於所有的值設置爲1,更新SQL Server中的表:如何將更新分塊到SQL Server?

TABLE 
DATE  ID  FLAG (more columns...) 
2016/01/01 1  0  ... 
2016/01/01 2  0  ... 
2016/01/02 3  0  ... 
2016/01/02 4  0  ... 
(etc) 

問題是,這個表包含數以億計的記錄,我已經被建議每次更新100,000行更新以避免阻塞其他進程。

我需要記住我更新哪些行,因爲有一些後臺進程會在FLAG處理完成後立即將FLAG翻轉回0。

有沒有人有如何做到這一點的建議? 每天的數據都有超過一百萬條記錄,所以我不能簡單地使用DATE作爲計數器來循環。我正在考慮使用ID

回答

1

假設日期欄和ID列做這個過程是連續的,你可以做一個簡單的循環。我的意思是,如果有記錄id=1 and date=2016-1-1那麼記錄id=2 date=2015-12-31不可能存在。如果您擔心鎖定/異常,您應該在WHILE塊中添加一個事務,並在失敗時提交或回滾。

@batchSize更改爲任何你認爲是經過一些實驗後。

DECLARE @currentId int, @maxId int, @batchSize int = 10000 

SELECT @currentId = MIN(ID), @maxId = MAX(ID) FROM YOURTABLE WHERE DATE >= '2016-01-01' 

WHILE @currentId < @maxId 
BEGIN 
    UPDATE YOURTABLE SET FLAG = 1 WHERE ID BETWEEN @currentId AND (@currentId + @batchSize) 
    SET @currentId = @currentId + @batchSize 
END 

由於本作更新將永遠不會旗相同的記錄爲1次,我不認爲有必要進行跟蹤觸摸時記錄,除非你通過手動停止過程中途。

您還應該確保ID列上有一個索引,以便每個更新語句中的檢索速度都很快。

+0

這個過程會在單獨的事務中更新嗎?還是會阻塞,直到整個循環完成? –

+0

@JinKim - 循環中的每個「UPDATE」語句執行都是它自己的事務。如果你想阻止,直到整個事情完成,你可以把它包裝在一個交易中,但正如我理解你的原始問題,這是你想要避免的。 – Igor

+0

就像一點,如果WHILE循環因任何原因(服務器崩潰或任何其他問題)崩潰,您將失去更新記錄的軌道。 – FLICKER

1

看起來很簡單的問題,或者我錯過了一些東西。

您可以創建一個臨時/永久表來跟蹤更新的行。

create tbl (Id int) -- or temp table based on your case 
insert into tbl values (0) 

declare @lastId int = (select Id from tbl) 

;with cte as (
    select top 100000 
    from YourMainTable 
    where Id > @lastId 
    ORDER BY Id 
) 
update cte 
set Flag = 1 

update tbl set Id = @lastId + 100000 

您可以在一個循環(除創建表的一部分)

0
create table #tmp_table 
(

    id int , 
    row_number int 
) 

insert into #tmp_table 
(

    id, 
    row_number 
) 

--logic to load records from base table 
select 
    bt.id, 
    row_number() over(partition by id order by id) as row_number 
from 
    dbo.bas_table bt 
where 
    --ur logic to limit the records 

declare @batch_size int = 100000; 
declare @start_row_number int,@end_row_number int; 
select 
    @start_row_number = min(row_number), 
    @end_row_number = max(row_number) 
from 
    #tmp_table 

while(@start_row_number < @end_row_number) 
begin 
    update top @batch_size 
     bt 
    set 
     bt.flag = 1 
    from 
     dbo.base_table bt 
     inner join #tmp_table tt on 
      tt.Id = bt.Id 
    where 
     bt.row_number between @start_row_number and (@start_row_number + @batch_size) 
    set @start_row_number = @start_row_number + @batch_size 
end