我有以下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执行本地过程。
答案 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;