怎麼樣大批量的更新數據而不影響正常業務
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腳本很好實現,或者結合臨時表,在臨時表中增加一個有序列性質的列,從小序列開始往大序列更新,記錄更新到的序列號即可。