Stream 同步錯誤之解決方案 ORA-00001 ORA-26787 ORA-26786

  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

相關文章
相關標籤/搜索