生產上要修改某個產品的產品代號, 而咱們系統是以產品爲中心的, 牽一髮而動全身, 涉及表幾乎覆蓋所有, 有些表數據量是至關大的, 達到千萬, 億級別.sql
單純的維護產品代號的 SQL 是不難的, 可是性能是最大的問題, 最後採用了 rowid+forall分批更新策略.oop
細節涉及: 性能
遊標(rowid) fetch
dbms_sql.Urowid_Table 優化
(異常聲明;) spa
fetch v_rowid_cursor bulk collect into v_rowid_table limit v_once_commit; ....net
forall i in 1 .. v_rowid_table.count save exceptions ...日誌
完整代碼:code
PL/SQL存儲過程分批更新↓blog
1 --rowid+forall批量更新 2 declare 3 v_total_count integer default 500000; --待更新目標記錄總計 4 v_once_commit integer default 10000; --單次提交量 5 v_pre_prod_code char(8) := '90310004'; 6 v_new_prod_code char(8) := '9031000X'; 7 v_dml_name varchar2(100) := 'update tb_cust_vol_list 20181215 01'; 8 v_suc_count integer := 0; --成功提交計數 9 v_err_count integer := 0; --失敗記錄數 10 v_curr_batch integer := 0; --當前提交批次 11 v_start_time date := sysdate; --開始時間 12 --待更新目標記錄rowids 13 cursor v_rowid_cursor is 14 select rowid from tb_cust_vol_list 15 where prod_code = v_pre_prod_code 16 order by rowid; --rowid排序,提升效率 17 v_rowid_table dbms_sql.Uv_rowid_table; --臨時單次rowid放置表 18 v_error exception; --異常聲明 19 pragma exception_init(v_error, -24381); --指定ora-錯誤碼 20 21 begin 22 --操做日誌 23 insert into tb_dml_log values ( 24 v_dml_name, 25 v_total_count, 26 v_once_commit, 27 v_start_time, 28 v_start_time, 29 0, 0, 0, 0, 0 30 ); 31 commit; 32 33 open v_rowid_cursor; --打開rowids遊標 34 loop 35 exit when v_rowid_cursor%notfound; 36 --臨時rowids表 37 fetch v_rowid_cursor bulk collect into v_rowid_table limit v_once_commit; 38 exit when v_rowid_table.count = 0; 39 40 begin 41 forall i in 1 .. v_rowid_table.count save exceptions 42 --rowid定位行更新 43 update tb_cust_vol_list set prod_code=v_new_prod_code where rowid=v_rowid_table(i); 44 exception 45 when v_error then --目標異常 46 dbms_output.put_line('ora-24381, error in array DML !'); 47 dbms_output.put_line('exception count: ' || sql%bulk_exceptions.count); 48 v_err_count := v_err_count + sql%bulk_exceptions.count; 49 when others then 50 dbms_output.put_line('ora-XXX error occurred !'); 51 dbms_output.put_line('exception count: ' || sql%bulk_exceptions.count); 52 v_err_count := v_err_count + sql%bulk_exceptions.count; 53 end; 54 55 v_suc_count := v_suc_count + v_rowid_table.count; 56 v_curr_batch := v_curr_batch + 1; 57 --更新log 58 update tb_dml_log a set 59 a.curr_time=sysdate, 60 a.curr_cost=ceil((sysdate-v_start_time)*24*60*60), 61 a.curr_batch=v_curr_batch, 62 a.process=v_suc_count/a.total_count, 63 a.suc_count=v_suc_count, 64 a.err_count=v_err_count 65 where a.dml_name=v_dml_name; 66 commit; 67 68 end loop; 69 70 dbms_output.put_line('total error count: ' || v_err_count); 71 end;
有幫助的博客:
https://blog.csdn.net/leinuo180/article/details/23344647
其餘考慮:
若是有的表目標數據實在太大, 就算上述優化依然很費時間, 可將此表的目標數據拆分, 好比按月, 按編號.. 分幾個腳本, 維護時並行執行.
查詢語句獲取全部目標數據的 rowid 時, 若是佔比很大(索引空間大), 能夠嘗試禁用索引查詢, 使用全表並行查(網上看到的,本人還何嘗試).
超大表要注意索引空間的維護.
@20190126更新
批量數據更新方式
1)表重建
即先新建複製表(create 新表(...) as select ... from 舊錶 where ...),而後刪除舊錶(drop),最後修改表名(rename 新表名 to 舊錶名),最後恢復表結構(約束,索引等)。
2)rowid+forall PL/SQL存儲過程更新
即上面的辦法
3)JDBC程序分批更新
優勢是DB無關性,性能也不慢,只是數據量太大(千萬級)時,力不從心。