如何使用execute immediate执行本地过程?

时间:2014-12-19 13:56:44

标签: sql oracle plsql

我有以下PL SQL Block:

WHENEVER SQLERROR EXIT 1
SET SERVEROUTPUT ON   

DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';

   PROCEDURE OPENLOG (file_name IN VARCHAR2)
   IS
   BEGIN
      NULL;
   END;    

BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
v_sql := 'BEGIN ' || p_procname || '(:a); END;';
EXECUTE IMMEDIATE v_sql USING IN f1;
END;
/

当我执行上面的块时,我收到错误:

DECLARE
*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'OPENLOG' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
ORA-06512: at line 19

但是,如果程序OPENLOG是程序包的一部分,那么它可以正常工作。

请告知如何使用动态SQL执行本地过程。

2 个答案:

答案 0 :(得分:3)

正如Amarillo所说,你无法动态执行本地定义的过程,因为它在动态部分将使用的SQL范围中不存在。

您描述的情况是,所有过程都在匿名块的DECLARE部分中定义,并且您正在运行一个查询,告诉您要执行哪些过程 - 并且可能还会为您提供要传递的参数。您可以使用if / else构造或case语句来执行相应的过程,例如:

DECLARE
  ...
BEGIN
  FOR data IN (SELECT procname, arg1, arg2, ... from <your_query>) LOOP
    CASE data.procname
      WHEN 'OPENLOG' THEN
        openlog(data.arg1);
      WHEN 'WRITELOG' THEN
        writelog(data.arg1, data.arg2);
      WHEN ...
        ...
      ELSE
         -- handle/report an invalid procedure name
         -- or skip the `ELSE` and let CASE_NOT_FOUND be thrown
    END CASE;
  END LOOP;
END;
/

每个程序只需要一个WHEN条件和适当的过程调用。您也可以使用ELSE来捕获任何意外的过程名称,或者抛出CASE_NOT_FOUND异常(ORA-06592),具体取决于您发生的情况。

答案 1 :(得分:2)

像这样使用:

DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';

   PROCEDURE OPENLOG (file_name IN VARCHAR2)
   IS
   BEGIN
      NULL;
   END;    

BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
openlog(f1);
END;

在这种情况下,您不需要在begin end中使用execute immediate,因为您在declare部分中有过程。

另一种方法是将过程创建为数据库对象,如下所示:

       CREATE PROCEDURE OPENLOG (file_name IN VARCHAR2)
       IS
       BEGIN
          NULL;
       END;  

你可以使用execute immediate:

DECLARE
v_sql VARCHAR2(500);
f1 VARCHAR2(20) := 'abc';
p_procname VARCHAR2 (30) := 'OPENLOG';

BEGIN
DBMS_OUTPUT.PUT_LINE('Begin');
v_sql := 'BEGIN ' || p_procname || '(:a); END;';
EXECUTE IMMEDIATE v_sql USING IN f1;
END;