【问题标题】:How to execute a local procedure using execute immedate?如何使用立即执行来执行本地过程?
【发布时间】:2014-12-19 13:56:43
【问题描述】:

我有以下 PL SQL 块:

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 执行本地过程。

【问题讨论】:

  • 是您的查询返回的整个过程的源代码,还是在您的匿名块中总是定义了几个过程并且查询只是告诉您要执行哪个?两者都有点奇怪,但可能有一种方法,具体取决于正在发生的事情。一个更完整的例子可能会有用。
  • 所有过程都定义在匿名块中。查询只是告诉执行哪些。

标签: sql oracle plsql


【解决方案1】:

正如 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)被抛出,这取决于如果发生这种情况您需要发生什么。

【讨论】:

    【解决方案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;
    

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

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

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

    你可以使用立即执行:

    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;
    

    【讨论】:

    • 我希望它只通过立即执行,因为我有一组作为查询结果的一部分返回的过程,我想执行所有这些过程。
    • 由于某些限制,它不能被创建,我只能将它作为本地过程的意思,没有“创建”。
    • 哇,在这种情况下,您应该联系您的数据库管理员并请求“创建过程”权限,因为我认为无法以动态方式执行在匿名块中声明的过程
    猜你喜欢
    • 2021-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    相关资源
    最近更新 更多