怎麼樣大批量的更新數據而不影響正常業務

怎麼樣大批量的更新數據而不影響正常業務

1、化整爲零
一般情況下,如果需要對一個表進行大批量的更新的時候,由於涉及到的記錄數很多,所以可能需要花費的時間也就很長,這種情況下,還採用一個單獨的update 語句來更新的話,就會造成長時間的加鎖,影響到業務。
簡單的一個例子,如要更新im_user表中的非空ID爲用戶表bmw_users中的ID,關聯字段爲im_user.login_id=bmw_users.nick,語句可以這樣寫

代碼:

 


update im_user i set i
.id=(select id from bmw_users u
where i
.login_id=u.nick
)
   
where i.id is not null
;
.


這個語句可以更新到幾百萬記錄,當然,耗費時間可能需要1小時以上,對於im_user這樣被頻繁更新的表來說,肯定是不現實的,所以,該語句可以改寫爲如下的PL/SQL塊。

代碼:

 


declare
  
row_num number := 0
;
begin
for c_usr in (select login_id from im_user t where id is null)
loop
   update im_user i set i
.id
=
     (
select id from bmw_users u where i.login_id = u.nick
)
   
where login_id = c_usr.login_id
;
   
row_num := row_num + 1
;
   if
mod(row_num,100) =
0 then
     commit
;
   
end
if;
end loop
;
commit
;
end
;
/
.


這樣的話,因爲每更新100條就提交1次,對錶的影響相對是很小的,而且,如果是一個語句,如果中途執行失敗,將導致回滾,同樣要耗費很長時間,但是這種情況下,因爲是一邊執行一邊提交,基本可以分很多次來操作,之間不會有影響。

2、巧用臨時表
很多情況下,需要更新的數據是根據很多條件判斷出來的,查詢很慢,但是更新的數據本身不多,比較快,這個時候,就可以考慮用臨時表,先把需要更新的數據(包括主鍵)放入到臨時表,然後根據主鍵更新,可能一個UPDATE語句就可以解決問題。
如支付寶遷移時,更新認證表數據:
先創建臨時表

代碼:

 


create table bmw_idauth_db1_20050704
as
select a.id,b.idauth_passdate from bmw_users a,
bmw_idauth b
    where a
.nick=b.
nick
      
and b.status=
'SUCCESS'
      
and b.idauth_passdate>=to_date('20050501','yyyymmdd'
);

create table account_db1_20050704
as
select b.account_no,a.
idauth_passdate
   from bmw_idauth_db1_20050704 a
,
bmw_payment_account b
          where a
.id=b.
user_id
            
and b.enabled_status='1'
;
.


然後根據臨時表來更新,因爲記錄數本身只在查詢獲得數據比較慢,而這裏更新就很快了。

代碼:

 


UPDATE
(SELECT a.idauth_passdate,
               
b.id_auth_date
,
               
b.
is_id_auth
          FROM account_db1_20050704 a
,
beyond_credit_info b
         WHERE a
.account_no = b.user_id||'0156')
x
   SET x
.id_auth_date = x.idauth_passdate
,
       
x.is_id_auth ='1'
;
.


另外一個方面,臨時表可以對需要更新的數據做備份,如果發現數據更新錯誤或者時間,可以回滾。如對需要更新的數據,先創建一個臨時備份表出來,這樣的話,如果更新失敗也可以回滾:

代碼:

 


create table tmp_table
as select id,name,address from test_table where ……;

update test_table t set name=?,address
=?
  
where id in (select id from tmp_table
);
.


或者
--where exists (select null from tmp_table tmp where tmp.id=t.id)
當然,如果臨時表的數據量也很大的話,也可以與方法1結合,在臨時表中做循環,如
for c_usr in (select id from tmp_table t) loop

其它很多小技巧,如斷點繼續(也就是更新失敗後,不用重新開始,從失敗點繼續更新)。採用方法1的PL/SQL腳本很好實現,或者結合臨時表,在臨時表中增加一個有序列性質的列,從小序列開始往大序列更新,記錄更新到的序列號即可。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章