从Oracle Web工具包迁移到Apex 4.2

时间:2014-07-08 14:05:11

标签: oracle oracle11g oracle-sqldeveloper oracle-apex

假设我有这样的程序

CREATE OR REPLACE PROCEDURE my_proc IS
var_empno emp.empno%type;
var_ename emp.ename%type;
var_bonus emp.bonus%type;
var_budget number;
var_budget := 100000;

CURSOR EMP_CURSOR IS
select empno, ename, bonus from emp order by empno;

BEGIN
htp.print('EMPLOYEE NUMBER   EMPLOYEE NAME    BONUS');

open EMP_CURSOR;
LOOP
fetch EMP_CURSOR into var_empno, var_ename,  var_bonus;
//------Give employee a extra $1,000 bonus if budget allows-----------.
EXIT when EMP_CURSOR%NOTFOUND;

IF (var_budget >= 1000) then
  var_budget := var_budget - 1000;
  var_bonus := var_bonus + 1000; 
END IF;

//----DBMS_OUTPUT.put_line(var_empno || ' ' || var_ename || ' ' || var_bonus);
htp.print(var_empno || ' ' || var_ename || ' ' || var_bonus);
END LOOP;

close EMP_CURSOR;
END;

该程序从最低empno排序的emp表中选择empno,name和bonus。如果var_budget有资金,游标会循环并向奖金变量增加1,000美元。该报告使用htp.print。

当然这只是一个简单的例子,我的报告html格式会更好。我们有100多个这样的程序。在APEX中处理这样的逻辑的最佳方法是什么,其中使用游标并且输出对于每一行是唯一的。使用简单的select语句,APEX似乎很好。但我不清楚如何转换这样的程序为APEX 4.2

1 个答案:

答案 0 :(得分:2)

为APEX报告区域调整Oracle REF CURSOR数据

  

以下解决方案是在apex.oracle.com上的Oracle Apex演示托管实例上开发的。在完成此操作时,托管的Apex发行版本为:4.2.5

根据您所查看的Oracle版本,REF CURSOR逻辑的实现在很小程度上发生了变化。我在Oracle-Base中找到了一个很好的讨论。值得注意的变化包括:

  1. 创建SYS_REFCURSOR类型,无需开发人员独立声明自己的自定义类型,以便在代码中引用REF CURSOR类型。
  2. 编程语言/平台(例如 Microsoft ADO Java )具有可用于打开和迭代存储在REF CURSOR中的记录的语法和结构。这可能就是为什么有很多像你这样的商店在REF CURSOR数据结构中有程序代码;它们很简单,并且与其他编程语言兼容。
  3. APEX报告区域定义 最适合使用SQL SELECT 查询定义的数据输入 即可。
  4. 从Oracle Web Toolkit中调整现有的基于REF CURSOR的PL / SQL代码

    以下是OP的示例程序,其中包含对初始更改的评论,以使其适用于APEX报告输出区域。

        CREATE OR REPLACE PROCEDURE my_proc (bonus_increase IN number, 
           result_data OUT sys_refcursor) IS
    
        -- (1) Replace Procedure Declaration to include output REF CURSOR
        -- CREATE OR REPLACE PROCEDURE my_proc IS
    
        -- (2) Remove variable references/placeholders used by procedure 
        --     for data output display reasons.
        -- var_empno emp.empno%type;
        -- var_ename emp.ename%type;
        -- var_bonus emp.bonus%type;
        -- var_budget number;
        -- var_budget := 100000;
    
        -- (3) A suggested practice to put a tolerance level within a 
        --     constant variable.
        --
        --     Removing literals from the SQL code segments improves 
        --     performance, because it allows the PL/SQL interpreter to 
        --     reuse the execution plans for multiple consecutive runs of 
        --     the cursor query.
    
        c_var_budget_limit    constant number:= 100000;
        l_var_budget          number;
    
        CURSOR EMP_CURSOR IS
        select empno, ename, bonus from emp order by empno;
    
        BEGIN
    
        -- (4) This task is reserved for APEX to handle in a REPORT REGION 
        --     definition.
        -- htp.print('EMPLOYEE NUMBER   EMPLOYEE NAME    BONUS');
    
        -- (5) I rewrote the cursor using the IMPLICIT cursor method.
    
        l_var_budget:= c_var_budget_limit;
    
        for result_data in EMP_CURSOR
    
        LOOP
           IF (l_var_budget >= bonus_increase) then
              l_var_budget := l_var_budget - bonus_increase;
              result_data.bonus:= result_data.bonus + bonus_increase;
           END IF;
    
        END LOOP;
    
        -- (6) The web toolkit output is no longer necessary.
        //----DBMS_OUTPUT.put_line(var_empno || ' ' || var_ename || ' ' 
        -- || var_bonus);
        -- htp.print(var_empno || ' ' || var_ename || ' ' || var_bonus);
    
        END;
    

    注释部分显示已删除显示和输出以及格式约定的负担。我在一篇文章中找到了关于如何使用REF CURSORS作为业务逻辑的封装查询的一个很好的参考:Using Ref Cursors Reference发布在“oracle-base.com”的参考文献中。

    在此参考之后建模的清理过程如下所示:

        CREATE OR REPLACE PROCEDURE my_proc_data (result_data OUT sys_refcursor) 
        IS
        BEGIN
        OPEN result_data FOR
           SELECT empno, ename, 0 as bonus
           FROM   emp
           ORDER BY empno ASC;
    
        END;
    

    打开并循环遍历游标内容的调用过程看起来类似于OP过程,采用匿名PL / SQL块的形式:

        DECLARE
           l_cursor  SYS_REFCURSOR;
           l_empno   emp.empno%TYPE;
           l_ename   emp.ename%TYPE;
           l_bonus   number;
    
           c_var_budget_limit    constant number:= 100000;
           c_bonus_increase      constant number:= 1000;
           l_var_budget          number;
    
        BEGIN
           my_proc_data (result_data => l_cursor);
           l_var_budget := c_var_budget_limit;
    
        LOOP 
           FETCH l_cursor
           INTO  l_empno, l_ename, l_bonus;
    
           IF (l_var_budget >= c_bonus_increase) then
              l_var_budget := l_var_budget - c_bonus_increase;
              l_bonus:= l_bonus + c_bonus_increase;
    
              DBMS_OUTPUT.PUT_LINE(to_char(l_empno) || ' | ' || l_ename || 
                 ' | ' || to_char(l_bonus));
    
           END IF;
    
        EXIT WHEN l_cursor%NOTFOUND;
        END LOOP;
        CLOSE l_cursor;
        END;
    

    结果输出:

        7369 | SMITH | 1000
        7499 | ALLEN | 1000
        7521 | WARD | 1000
        7566 | JONES | 1000
        7654 | MARTIN | 1000
        7698 | BLAKE | 1000
        7782 | CLARK | 1000
        7788 | SCOTT | 1000
        7839 | KING | 1000
        7844 | TURNER | 1000
        7876 | ADAMS | 1000
        7900 | JAMES | 1000
        7902 | FORD | 1000
        7934 | MILLER | 1000
        7934 | MILLER | 2000
    
        Statement processed.
    

    这不是最终解决方案,请记住,数据需要以SELECT语句的形式提供给APEX。

    使用Oracle PL / SQL集合通过Apex报告区域提供REF CURSOR数据

    进一步准备原始样本程序以用于Apex页面区域报告的一些其他更改:

    1. 添加了两个新的SQL对象类型:EMPLOYEE_RECORD_TYPEEMP_OUTPUT_TABLE_TYPE
    2. 更改为PL / SQL FUNCTION对象类型而非PROCEDURE对象类型。
    3. 将输出数据类型更改为NESTED TABLE集合类型。 (这允许我们使用直接SQL来查询存储在此集合中的输出数据。)
    4. 合并了REF CURSOR和报告输出查询的分隔代码。
    5. 将用于测试第一部分的Anonymous PL / SQL块转换为FUNCTION对象。
    6. 从OP更改(减少)MAX BUDGET值,以便我们可以看到工作中的循环逻辑(即用尽预算资金以奖励员工奖金)
    7. 以下是修订后的代码:

      SQL集合和对象类型定义(DDL)

          CREATE OR REPLACE TYPE employee_record_type AS object (
             empno   number,
             ename   varchar2(10),
             bonus   number
          );
      
          CREATE OR REPLACE TYPE emp_output_table_type IS TABLE OF
             employee_record_type;
      

      新的PL / SQL函数定义(包含REF CURSOR)

          create or replace FUNCTION my_bonuses RETURN
             emp_output_table_type IS
      
          -- table collection type declared and initialized here:
             l_output  emp_output_table_type:= emp_output_table_type();
             l_row_index   pls_integer:= 0;  
      
             c_var_budget_limit    constant number:= 100000;
             c_bonus_increase      constant number:= 1000;
             l_var_budget          number;
      
             cursor l_cursor is
                select empno, ename, 0 as bonus
                from   emp
             order by empno ASC;
      
          BEGIN
             l_var_budget := c_var_budget_limit;
      
          FOR i in l_cursor
             LOOP
                l_row_index := l_row_index + 1;
                l_output.extend;
      
                l_output(l_row_index):= employee_record_type(i.empno,
                   i.ename, i.bonus);
      
          IF (l_var_budget >= c_bonus_increase) then
             l_var_budget := l_var_budget - c_bonus_increase;
             l_output(l_row_index).bonus:= l_output(l_row_index).bonus
                 + c_bonus_increase;
      
          END IF;
      
          END LOOP;
          RETURN l_output;
          END;
      

      从SQL客户端查询时功能MY_BONUSES

      Test Query of New Function with Nested Table Collection Output

      这是将在Apex页面报告定义中引用的SQL,如区域定义配置页面的“Region Source”部分。

      比较:源表数据与REF CURSOR查询结果

      Results From Collection Data Type Output

      一些结束评论和讨论

      考虑到Oracle 9i之间RDBMS产品版本的飞跃,包括11g和12c,这个例子为大量优化留出了空间。一些需要考虑的想法:

      1. 在将数据从ref-cursors加载到Oracle集合类型时使用批量绑定操作和方法。
      2. 额外的模块化。在开发此解决方案期间,您可以看到OP过程my_proc的组件移动了一点。您的转换工作可能会受益于更好的组织使用包和流程分离(有意义的地方)。
      3. 阅读有关Oracle PL / SQL集合的文档。您将学到一些重要的区别: 一个。可以使用SQL直接查询某些集合类型。 湾PL / SQL vs模式级别定义的复合数据类型各有其局限性......
      4. 一般情况下,明智地选择,或者只是坚持使用这个示例,因为它应该可以帮助您将现有的PL / SQL代码库转换为REF CURSOR驱动的参数/输出。

        冠!