stream是 oracle 11g 支持的數據同步技術, 雖然該技術已經不是什麼新技術, 但目前國內採用該技術開發的軟件很少見. stream 同步軟件項目參與近一年, 近期軟件上線實施, 效果不是很理想. 同步過程當中會偶爾出現 ORA-00001, ORA-26786, ORA-26787 等常見錯誤. 通過幾天的的研究, 開發了一個守護平臺,採用 java 平臺和存儲過程相結合的方式自動處理以上錯誤. 我寫的這些算法具備通用性, 不須要手工指定刪除表的列,經過實踐具備較好的效果. 相對於set_update_conflict_handler這個方法,簡單易用. java 平臺代碼便不開源了, 其實核心的思想仍是在存儲過程當中, 特分享給你們, 共同進步.java
自定義 type算法
create or replace type myvarray_list as varray(300) of varchar2(50)
CREATE OR REPLACE PROCEDURE EXECUTE_TRANSACTION_1(applyname IN VARCHAR2,ltxnid IN VARCHAR2) IS i NUMBER; x NUMBER; loopdog NUMBER; txnid VARCHAR2(30); source VARCHAR2(128); msgno NUMBER; msgcnt NUMBER; errno NUMBER; errmsg VARCHAR2(2000); lcr ANYDATA; rowlcr SYS.LCR$_ROW_RECORD; typenm VARCHAR2(61); res NUMBER; command VARCHAR2(10); old_values SYS.LCR$_ROW_LIST; new_values SYS.LCR$_ROW_LIST; v_code NUMBER; v_errm VARCHAR2(1024); object_owner VARCHAR2(30); object_name VARCHAR2(40); key_column myvarray_list; remove_column myvarray_list; remove_flag NUMBER; remove_count NUMBER; BEGIN SELECT LOCAL_TRANSACTION_ID, SOURCE_DATABASE, MESSAGE_NUMBER, MESSAGE_COUNT, ERROR_NUMBER, ERROR_MESSAGE INTO txnid, source, msgno, msgcnt, errno, errmsg FROM DBA_APPLY_ERROR WHERE LOCAL_TRANSACTION_ID = ltxnid; DBMS_OUTPUT.PUT_LINE(' --- Local Transaction ID: ' || txnid); DBMS_OUTPUT.PUT_LINE(' --- Source Database: ' || source); DBMS_OUTPUT.PUT_LINE(' ---Error in Message: '|| msgno); DBMS_OUTPUT.PUT_LINE(' ---Error Number: '||errno); DBMS_OUTPUT.PUT_LINE(' ---Message Text: '||errmsg); i := msgno; loopdog :=0; WHILE i <= msgcnt LOOP loopdog :=loopdog+1; DBMS_OUTPUT.PUT_LINE('---message: ' || i); lcr := DBMS_APPLY_ADM.GET_ERROR_MESSAGE(i, txnid); -- gets the LCR typenm := lcr.GETTYPENAME(); DBMS_OUTPUT.PUT_LINE('type name: ' || typenm); IF (typenm = 'SYS.LCR$_ROW_RECORD') THEN res := lcr.GETOBJECT(rowlcr); command := rowlcr.GET_COMMAND_TYPE(); DBMS_OUTPUT.PUT_LINE('command type name: ' || command); IF command = 'INSERT' THEN rowlcr.SET_COMMAND_TYPE('DELETE'); new_values := rowlcr.GET_VALUES('new'); -- Set the old values in the row LCR to the new values in the row LCR rowlcr.SET_VALUES('old', new_values); -- Set the old values in the row LCR to NULL rowlcr.SET_VALUES('new', NULL); old_values := rowlcr.GET_VALUES('old'); -- Apply the row LCR as an DELETE FROM the table object_name :=rowlcr.GET_OBJECT_NAME(); object_owner :=rowlcr.GET_OBJECT_OWNER(); key_column := myvarray_list(); --init array. i :=1; FOR emm IN (select DISTINCT CO.COLUMN_NAME from DBA_cons_columns CO, dba_constraints PK where CO.constraint_name = PK.constraint_name AND CO.OWNER = PK.OWNER AND PK.constraint_type IN ('P','C','U') and PK.table_name = object_name AND PK.OWNER=object_owner) LOOP DBMS_OUTPUT.PUT_LINE('key column ' || emm.COLUMN_NAME); key_column.extend; key_column(i) := emm.COLUMN_NAME; i :=i+1; END LOOP; remove_column := myvarray_list(); --init array. remove_count := 1; FOR i in 1..old_values.count LOOP IF old_values(i) IS NOT NULL THEN --DBMS_OUTPUT.PUT_LINE('old(' || i || '): ' || old_values(i).column_name); x :=1; remove_flag :=1; WHILE x <= key_column.count AND remove_flag=1 loop --dbms_output.put_line('key_column('||x||')='||key_column(x)); IF old_values(i).column_name = key_column(x) THEN remove_flag :=0; END IF; x :=x +1; END loop; IF remove_flag = 1 then remove_column.extend; remove_column(remove_count) := old_values(i).column_name; remove_count :=remove_count+1; END IF; END IF; END LOOP; FOR x in 1..remove_column.count loop dbms_output.put_line('remove_column('||x||')='||remove_column(x)); rowlcr.DELETE_COLUMN(remove_column(x),'old'); END loop; rowlcr.EXECUTE(true); END IF; BEGIN --dbms_apply_adm.execute_all_errors(applyname); dbms_apply_adm.EXECUTE_ERROR(ltxnid); return; EXCEPTION when OTHERS then SELECT MESSAGE_NUMBER INTO i FROM DBA_APPLY_ERROR WHERE LOCAL_TRANSACTION_ID = ltxnid; v_code := SQLCODE; v_errm := SUBSTR(SQLERRM, 1, 1024); DBMS_OUTPUT.PUT_LINE('Error message: ' || v_errm); IF loopdog > msgcnt then RAISE_APPLICATION_ERROR(-20002,'Insert or Delete error. please check your procedure.'); ELSIF v_code = -1 then DBMS_OUTPUT.PUT_LINE('Error code(-1): ' || v_code); --null; ELSIF v_code = -26786 then DBMS_OUTPUT.PUT_LINE('Error code(-26786): ' || v_code); RAISE_APPLICATION_ERROR(-20786,v_errm); ELSIF v_code = -26787 then DBMS_OUTPUT.PUT_LINE('Error code(-26787): ' || v_code); RAISE_APPLICATION_ERROR(-20787,v_errm); ELSE RAISE_APPLICATION_ERROR(-20000,v_errm); END IF; END; END IF; END LOOP; --loop END EXECUTE_TRANSACTION_1; --須要這兩個 權限 --grant select on dba_constraints to DWESBSTREAMUSER; --granT select on DBA_cons_columns to DWESBSTREAMUSER;
ORA-00001 錯誤是目標端存在數據,可是存在衝突列, 這裏儲存儲過程的思想是根據惟一列刪除目標端的對應行, 並將LCR 是old 值插入到目標端.oracle
-----------------------------------------------------------------------------------------------------------------------------------app
CREATE OR REPLACE PROCEDURE EXECUTE_TRANSACTION_26786(applyname IN VARCHAR2,ltxnid IN VARCHAR2) IS i NUMBER; x NUMBER; loopdog NUMBER; txnid VARCHAR2(30); source VARCHAR2(128); msgno NUMBER; msgcnt NUMBER; errno NUMBER; errmsg VARCHAR2(2000); lcr ANYDATA; rowlcr SYS.LCR$_ROW_RECORD; typenm VARCHAR2(61); res NUMBER; command VARCHAR2(10); old_values SYS.LCR$_ROW_LIST; new_values SYS.LCR$_ROW_LIST; v_code NUMBER; v_errm VARCHAR2(1024); object_owner VARCHAR2(30); object_name VARCHAR2(40); key_column myvarray_list; remove_column myvarray_list; remove_flag NUMBER; remove_count NUMBER; BEGIN SELECT LOCAL_TRANSACTION_ID, SOURCE_DATABASE, MESSAGE_NUMBER, MESSAGE_COUNT, ERROR_NUMBER, ERROR_MESSAGE INTO txnid, source, msgno, msgcnt, errno, errmsg FROM DBA_APPLY_ERROR WHERE LOCAL_TRANSACTION_ID = ltxnid; DBMS_OUTPUT.PUT_LINE('--- Local Transaction ID: ' || txnid); DBMS_OUTPUT.PUT_LINE('--- Source Database: ' || source); DBMS_OUTPUT.PUT_LINE('---Error in Message: '|| msgno); DBMS_OUTPUT.PUT_LINE('---Error Number: '||errno); DBMS_OUTPUT.PUT_LINE('---Message Text: '||errmsg); i := msgno; loopdog :=0; WHILE i <= msgcnt LOOP loopdog :=loopdog+1; DBMS_OUTPUT.PUT_LINE('---message: ' || i); lcr := DBMS_APPLY_ADM.GET_ERROR_MESSAGE(i, txnid); -- gets the LCR typenm := lcr.GETTYPENAME(); DBMS_OUTPUT.PUT_LINE('type name: ' || typenm); IF (typenm = 'SYS.LCR$_ROW_RECORD') THEN res := lcr.GETOBJECT(rowlcr); command := rowlcr.GET_COMMAND_TYPE(); DBMS_OUTPUT.PUT_LINE('command type name: ' || command); IF command IN ('DELETE','UPDATE') THEN rowlcr.SET_COMMAND_TYPE('DELETE'); old_values := rowlcr.GET_VALUES('old'); -- Set the new values in the row LCR to NULL rowlcr.SET_VALUES('new', NULL); -- Apply the row LCR as an DELETE FROM the table -- object_name :=rowlcr.GET_OBJECT_NAME(); object_owner :=rowlcr.GET_OBJECT_OWNER(); --------------------------------------------------------------------------------------- key_column := myvarray_list(); --init array. i :=1; FOR emm IN (select DISTINCT CO.COLUMN_NAME from DBA_cons_columns CO, dba_constraints PK where CO.constraint_name = PK.constraint_name AND CO.OWNER = PK.OWNER AND PK.constraint_type IN ('P','C','U') and PK.table_name = object_name AND PK.OWNER=object_owner) LOOP DBMS_OUTPUT.PUT_LINE('key column ' || emm.COLUMN_NAME); key_column.extend; key_column(i) := emm.COLUMN_NAME; i :=i+1; END LOOP; ----------------------------------------------------------------------------------- remove_column := myvarray_list(); --init array. remove_count := 1; FOR i in 1..old_values.count LOOP IF old_values(i) IS NOT NULL THEN --DBMS_OUTPUT.PUT_LINE('old(' || i || '): ' || old_values(i).column_name); x :=1; remove_flag :=1; WHILE x <= key_column.count AND remove_flag=1 loop --dbms_output.put_line('key_column('||x||')='||key_column(x)); IF old_values(i).column_name = key_column(x) THEN remove_flag :=0; END IF; x :=x +1; END loop; IF remove_flag = 1 then remove_column.extend; remove_column(remove_count) := old_values(i).column_name; remove_count :=remove_count+1; END IF; END IF; END LOOP; ----------------------------------------------------------------------------- FOR x in 1..remove_column.count loop dbms_output.put_line('remove_column('||x||')='||remove_column(x)); rowlcr.DELETE_COLUMN(remove_column(x),'old'); END loop; rowlcr.EXECUTE(true); DBMS_OUTPUT.PUT_LINE('DELETE target old new '); --- INSERT NEW DATA-- rowlcr.SET_COMMAND_TYPE('INSERT'); rowlcr.SET_VALUES('new',old_values); rowlcr.SET_VALUES('old',NULL); rowlcr.EXECUTE(true); DBMS_OUTPUT.PUT_LINE('insert source old new'); END IF; BEGIN --dbms_apply_adm.execute_all_errors(applyname); dbms_apply_adm.EXECUTE_ERROR(ltxnid); return; EXCEPTION when OTHERS then SELECT MESSAGE_NUMBER INTO i FROM DBA_APPLY_ERROR WHERE LOCAL_TRANSACTION_ID = ltxnid; v_code := SQLCODE; v_errm := SUBSTR(SQLERRM, 1, 1024); DBMS_OUTPUT.PUT_LINE('Error message: ' || v_errm); IF loopdog > msgcnt then RAISE_APPLICATION_ERROR(-20002,'Insert or Delete error. please check your procedure.'); ELSIF v_code = -26786 then DBMS_OUTPUT.PUT_LINE('Error code(-26786): ' || v_code); ELSIF v_code = -26787 then DBMS_OUTPUT.PUT_LINE('Error code(-26787): ' || v_code); RAISE_APPLICATION_ERROR(-20787,v_errm); ELSIF v_code = -1 then DBMS_OUTPUT.PUT_LINE('Error code(-1): ' || v_code); RAISE_APPLICATION_ERROR(-20001,v_errm); ELSE RAISE_APPLICATION_ERROR(-20000,v_errm); END IF; END; END IF; END LOOP; END EXECUTE_TRANSACTION_26786;
ORA-26786 思想與 ORA-00001 思想相似, 再也不贅述.oop
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------spa
CREATE OR REPLACE PROCEDURE EXECUTE_TRANSACTION_26787(applyname IN VARCHAR2,ltxnid IN VARCHAR2) IS i NUMBER; loopdog NUMBER; txnid VARCHAR2(30); source VARCHAR2(128); msgno NUMBER; msgcnt NUMBER; errno NUMBER; errmsg VARCHAR2(2000); lcr ANYDATA; rowlcr SYS.LCR$_ROW_RECORD; typenm VARCHAR2(61); res NUMBER; command VARCHAR2(10); old_values SYS.LCR$_ROW_LIST; new_values SYS.LCR$_ROW_LIST; v_code NUMBER; v_errm VARCHAR2(1024); BEGIN SELECT LOCAL_TRANSACTION_ID, SOURCE_DATABASE, MESSAGE_NUMBER, MESSAGE_COUNT, ERROR_NUMBER, ERROR_MESSAGE INTO txnid, source, msgno, msgcnt, errno, errmsg FROM DBA_APPLY_ERROR WHERE LOCAL_TRANSACTION_ID = ltxnid; DBMS_OUTPUT.PUT_LINE('--- Local Transaction ID: ' || txnid); DBMS_OUTPUT.PUT_LINE('--- Source Database: ' || source); DBMS_OUTPUT.PUT_LINE('---Error in Message: '|| msgno); DBMS_OUTPUT.PUT_LINE('---Error Number: '||errno); DBMS_OUTPUT.PUT_LINE('---Message Text: '||errmsg); i := msgno; loopdog :=0; WHILE i <= msgcnt LOOP loopdog :=loopdog+1; DBMS_OUTPUT.PUT_LINE('--message: ' || i); lcr := DBMS_APPLY_ADM.GET_ERROR_MESSAGE(i, txnid); -- gets the LCR --print_lcr(lcr); typenm := lcr.GETTYPENAME(); DBMS_OUTPUT.PUT_LINE('type name: ' || typenm); IF (typenm = 'SYS.LCR$_ROW_RECORD') THEN res := lcr.GETOBJECT(rowlcr); command := rowlcr.GET_COMMAND_TYPE(); DBMS_OUTPUT.PUT_LINE('command type name: ' || command); IF command = 'DELETE' THEN -- Set the command_type in the row LCR to INSERT rowlcr.SET_COMMAND_TYPE('INSERT'); old_values := rowlcr.GET_VALUES('old'); -- Set the old values in the row LCR to the new values in the row LCR rowlcr.SET_VALUES('new', old_values); -- Set the old values in the row LCR to NULL rowlcr.SET_VALUES('old', NULL); -- Apply the row LCR as an INSERT into the hr.emp_del table rowlcr.EXECUTE(true); ELSIF command = 'UPDATE' THEN BEGIN old_values := rowlcr.GET_VALUES('old'); new_values := rowlcr.GET_VALUES('new'); rowlcr.EXECUTE(true); rowlcr.SET_VALUES('new', old_values); rowlcr.SET_VALUES('old', new_values); rowlcr.EXECUTE(true); EXCEPTION when OTHERS then rowlcr.SET_COMMAND_TYPE('INSERT'); rowlcr.SET_VALUES('new', old_values); -- Set the old values in the row LCR to NULL rowlcr.SET_VALUES('old', NULL); rowlcr.EXECUTE(true); END; END IF; BEGIN --dbms_apply_adm.execute_all_errors(applyname); dbms_apply_adm.execute_error(ltxnid); return; EXCEPTION when OTHERS then SELECT MESSAGE_NUMBER INTO i FROM DBA_APPLY_ERROR WHERE LOCAL_TRANSACTION_ID = ltxnid; v_code := SQLCODE; v_errm := SUBSTR(SQLERRM, 1, 1024); DBMS_OUTPUT.PUT_LINE('Error message: ' || v_errm); IF loopdog > msgcnt then RAISE_APPLICATION_ERROR(-20002,'Insert or Delete error. please check your procedure.'); ELSIF v_code = -26787 then DBMS_OUTPUT.PUT_LINE('Error code(-26787): ' || v_code); --null; ELSIF v_code = -26786 then DBMS_OUTPUT.PUT_LINE('Error code(-26786): ' || v_code); RAISE_APPLICATION_ERROR(-20786,v_errm); ELSIF v_code = -1 then DBMS_OUTPUT.PUT_LINE('Error code(-1): ' || v_code); RAISE_APPLICATION_ERROR(-20001,v_errm); ELSE RAISE_APPLICATION_ERROR(-20001,v_errm); END IF; END; END IF; END LOOP; END EXECUTE_TRANSACTION_26787;
ORA-26787 處理方法相對簡單, 由於數據不存在引發的, 只要把LCR 中old 字段插入便可.code
參考 :server
-- https://docs.oracle.com/cd/B28359_01/server.111/b28321/strms_apmon.htmhtm
-- https://docs.oracle.com/cd/B10501_01/server.920/a96571/capappdemo.htmblog
-- https://docs.oracle.com/cd/B19306_01/appdev.102/b14258/t_lcr.htm#i997627