2016-08-11 130 views
1

我有要求在同一时间跨多个数据库运行SP,其中一部分是从每个数据库中删除一些重复记录。现在,由于SP可以多次运行,我已经包含了一个备份表以及在SP连续运行两次的情况下截断和删除它所需的内容。Oracle - 使用dbms_utility.exec_ddl_statement不能正确执行的游标

现在,因为我通过DBLINK创建表,我研究过我需要使用dbms_utility.exec_ddl_statement - 但在这种情况下,即使过程执行,截断和放置查询似乎什么都不做,因为当我第二次运行SP时,它告诉我备份表的名称已被使用(即使我在CREATE之前包含了DROP执行)。

loop 
    fetch v_data into v_database_name; 
    exit when v_data%NOTFOUND; 
    sql_update := 'BEGIN' 
       ||' EXECUTE IMMEDIATE ''truncate table iSecurity2_dupes_bak'';' 
       ||' EXCEPTION' 
       ||' WHEN OTHERS THEN' 
       ||'  IF SQLCODE != -942 THEN' 
       ||'   RAISE;' 
       ||'  END IF;' 
       ||' END;'; 
    execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
    commit; 
    sql_update := 'BEGIN' 
       ||' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';' 
       ||' EXCEPTION' 
       ||' WHEN OTHERS THEN' 
       ||'  IF SQLCODE != -942 THEN' 
       ||'   RAISE;' 
       ||'  END IF;' 
       ||' END;'; 
    execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
    commit; 
    sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2'; 
    execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
commit; 
................. 

ORA-00955: name is already used by an existing object 
ORA-06512: at "SYS.DBMS_UTILITY", line 478 
ORA-06512: at line 1 
ORA-06512: at "database.procedure_name", line 53 
ORA-06512: at line 2 

包括GLOBAL TEMP表删除,插入,更新和创建的游标的其余部分似乎工作得很好,一切都执行。如果我手动删除备份表,即使执行失败的CREATE。

我困惑:(

UPDATE 2016年8月12日

与@乔恩海勒提供帮助,我能够改变我的代码在其下方,只要工程,我使用一个静态名称为DB_LINK但是当我尝试使用该变量失败 尝试了以下两个版本,但仍然不能运行,但我一直在修改它们 - 我在这里丢失了什么?

注意:现在,我添加了alter session,因为没有它,重新运行orig由于ORA-04062,进程程序保持失败:过程“cw_drop_table”的时间戳已更改;

1版

loop 
    fetch v_data into v_database_name; 
    exit when v_data%NOTFOUND; 
    sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE'; 
    execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
    commit; 
     begin 
         [email protected]_database_name (
          q'[ 
          create or replace procedure cw_drop_table is sql_drop varchar2(2000); 
           begin 
            sql_drop := 'BEGIN' 
             ||' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';' 
             ||' EXCEPTION' 
             ||' WHEN OTHERS THEN IF SQLCODE != -942 THEN NULL; END IF; END;'; 
            execute immediate sql_drop; 
            commit; 
           end; ]');  
          execute immediate 'begin [email protected]'||v_database_name||'; end;'; 
     end; 
    sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2'; 
    execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
    commit; 

PLS-00352: Unable to access another database 'V_DATABASE_NAME' 
PLS-00201: identifier '[email protected]_DATABASE_NAME' must be declared 
PL/SQL: Statement ignored 

第二版

loop 
     fetch v_data into v_database_name; 
     exit when v_data%NOTFOUND; 
     sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE'; 
     execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
     commit; 
     declare v_db_name varchar2(100); 
      begin select v_database_name into v_db_name from dual;      
       execute immediate '[email protected]'||v_db_name||' (' 
          ||' q''[ ' 
          ||' create or replace procedure cw_drop_table is sql_drop varchar2(2000);' 
          ||'  begin ' 
          ||'   sql_drop := ''BEGIN'' ' 
          ||'    ||''  EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'' ' 
          ||'    ||'' EXCEPTION'' ' 
          ||'    ||''  WHEN OTHERS THEN IF SQLCODE != -942 THEN NULL; END IF; END;''; ' 
          ||'   execute immediate sql_drop;' 
          ||'   commit;' 
          ||'  end; ]''); ' 
          ||' execute immediate ''begin [email protected]'||v_db_name||'; end;''; '; 
      end; 
     sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2'; 
     execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
    commit; 

PLS-00103: Encountered the symbol "DROP" when expecting one of the following: 

* & = - + ; </> at in is mod remainder not rem 
<an exponent (**)> <> or != or ~= >= <= <> and or like LIKE2_ 
LIKE4_ LIKEC_ between || member SUBMULTISET_ 

SOLUTION

多沉思后和淋浴我放弃了上述方法,并与下面去了。不知道为什么我没有早点考虑它:|

注:如果有人通过这个啰嗦的问题永远读,知道我做错了什么在2016年8月12日更新,我很想找到:)

loop 
     fetch v_data into v_database_name; 
     exit when v_data%NOTFOUND; 
     sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE'; 
     execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 
     commit; 
     begin  
      sql_update:='DROP TABLE iSecurity2_dupes_bak'; 
      execute immediate 'begin [email protected]'||v_database_name||'(:sql_update); end;' using sql_update; 

      EXCEPTION 
       WHEN OTHERS THEN 
        IF SQLCODE = -942 THEN 
        NULL; -- suppresses ORA-00942 exception 
        ELSE 
        RAISE; 
        END IF; 
     END; 

回答

2

DBMS_UTILITY.EXEC_DDL_STATEMENT只运行可靠DDL。如果您尝试使用PL/SQL块运行它,它将会以静默方式失败并且不会运行任何内容。

这可以通过运行一个显然失败的PL/SQL块来演示。 以下的代码应为生成ORA-01476: divisor is equal to zero。但相反,它什么都不做。

begin 
    [email protected](
     q'[declare v_test number; begin v_test := 1/0; end;]' 
    ); 
end; 
/

使用临时过程远程运行PL/SQL块。用DBMS_UTILITY.EXEC_DDL_STATEMENT创建过程,然后用本机动态SQL调用它。

begin 
    [email protected](
     q'[ 
      create or replace procedure test_procedure 
      is 
       v_test number; 
      begin 
       v_test := 1/0; 
      end; 
     ]' 
    ); 
    execute immediate 'begin [email protected]; end;'; 
end; 
/

RESULTS: 

ORA-01476: divisor is equal to zero 
ORA-06512: at "JHELLER.TEST_PROCEDURE", line 5 
ORA-06512: at line 1 
ORA-06512: at line 12 

我认为这种行为是一个错误。 Oracle应该抛出一个错误,而不是简单地不做任何事情。


欢迎来到拼接地狱。当它们嵌入4层时,字符串会变得杂乱。但有几件事情可以让生活更轻松:

  1. 使用嵌套替代引用机制。例如,q'[ ... ]',内部为q'<...>'
  2. 使用多行字符串。不需要连接多行,只需使用一个字符串即可。
  3. 使用额外的间距来帮助识别字符串的开始和结束。当事情变得疯狂时,值得把一个字符串分隔符放在一行上,这样一切都很容易排列。
  4. 使用REPLACE而不是拼接。

我使用这些提示重新格式化了代码的一部分。 Stackoverflow不理解替代的引用机制,但字符串应该在一个好的Oracle SQL编辑器中看起来更好。

declare 
    v_db_name varchar2(30) := 'myself'; 
    sql_update varchar2(32767); 
begin 
    execute immediate replace(
    q'[ 
     begin 
      [email protected]#DB_NAME# 
      (
       q'< 
        create or replace procedure cw_drop_table is 
         sql_drop varchar2(2000); 
        begin 
         sql_drop := 
         q'{ 
          BEGIN 
           EXECUTE IMMEDIATE 'DROP TABLE iSecurity2_dupes_bak'; 
          EXCEPTION WHEN OTHERS THEN 
           IF SQLCODE != -942 THEN 
            NULL; 
           END IF; 
          END; 
         }'; 
         execute immediate sql_drop; 
        end; 
       >' 
      ); 
      execute immediate 'begin [email protected]#DB_NAME#; end;'; 
     end; 
    ]', '#DB_NAME#', v_db_name); 

    sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2'; 
    execute immediate 'begin [email protected]'||v_db_name|| 
     '(:sql_update); end;' using sql_update; 
    commit; 
end; 
/
+0

感谢虽然我的困惑在于为什么CREATE临时表PL/SQL块与TRUNCATE和DROP所述表一起工作得很好,而上面的静默失败了? – EkeshOkor

+0

@EkeshOkor CREATE ...是DDL,但是BEGIN ...不是DDL,尽管它可能包含* DDL。您可能想查看[SQL语句类型](http://docs.oracle.com/database/121/SQLRF/statements_1001.htm#SQLRF30001)。它不包含PL/SQL,但是从手册的其他部分来看,它暗示'PLSQL_BLOCK'是一个单独的语句类型。 –

+0

嗯..这是有道理的。 :) – EkeshOkor