【问题标题】:Moving from Oracle web toolkit to Apex 4.2从 Oracle Web 工具包迁移到 Apex 4.2
【发布时间】:2014-08-29 07:29:29
【问题描述】:

假设我有这样的程序

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 中处理此类逻辑的最佳方法是什么。 APEX 似乎很适合简单的选择语句。但我不清楚如何为 APEX 4.2 转换这样的程序

【问题讨论】:

  • 好消息是您可以回收基于引用游标的存储过程。但是需要注意的是,需要进行一些返工和准备工作,因为大多数 Apex 原生设计元素都设置为使用函数或 SQL SELECT 查询输出。在下面查看我发布的回复,了解您面前的任务的演练...

标签: oracle oracle11g oracle-sqldeveloper oracle-apex


【解决方案1】:

为 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 ADOJava 等编程语言/平台的语法和结构能够打开和迭代存储在 REF CURSOR 中的记录。这可能就是为什么有很多像你这样的商店在REF CURSOR 数据结构中使用程序代码的原因;它们简单且与其他编程语言兼容。
  3. APEX 报告区域定义最适合由 SQL 定义的数据输入 SELECT 查询强>。

从 Oracle Web Toolkit 改编现有的基于 REF CURSOR 的 PL/SQL 代码

这是来自 OP 的示例过程,其中 cmets 进行了初始更改,以使其适用于 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. 将用于测试第一部分的匿名 PL/SQL 块转换为 FUNCTION 对象。
  6. 从 OP 更改(降低)了 MAX BUDGET 值,以便我们可以看到工作中的循环逻辑(即,用完预算资金来奖励员工)

这是修改后的代码:

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

这是将在 Apex 页面报告定义中引用的 SQL,就像在区域定义配置页面的“区域源”部分中一样。

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

一些结束语和讨论

鉴于 Oracle 9i 之间的 RDBMS 产品版本的飞跃,此示例为大量优化留出了空间,直到并包括 11g 和 12c。一些需要考虑的附带想法:

  1. 在将数据从引用游标加载到 Oracle 集合类型时使用批量绑定操作和方法。
  2. 额外的模块化。您可以看到 OP 过程 my_proc 的组件在此解决方案的开发过程中发生了一些变化。您的转换工作可能会受益于使用包和流程分离(在有意义的情况下)更好地组织。
  3. 阅读有关 Oracle PL/SQL 集合的文档。您将了解一些重要的区别: 一种。有些集合类型可以直接用 SQL 查询。 湾。 PL/SQL 中定义的复合数据类型与架构级别定义的复合数据类型各有其局限性...

一般来说,明智地选择,或者只是坚持这个例子,因为它应该让你在转换现有的 PL/SQL 代码库时使用REF CURSOR 驱动的参数/输出。

前进!

【讨论】:

  • “创建类型employee_record_type”部分让我很难过。如果这是唯一的方法,我将不得不创建超过 100 个。每当我们修改列顺序时,我们必须记住找到相关的“类型”。我想我宁愿使用临时表或更喜欢输出表。我不知道,我知道我仍然需要调用一个程序。我了解 Apex 与 SELECTS 配合得很好,但是否可以调用一个过程而不是在 APEX 中进行选择?这样我就可以运行 proc,向表中插入行并进行选择。只有 3 或 4 个用户,所以运行报告的人
  • ..同时不会发生。这些报告是类似于帮助报告的仪表板。不是关键的业务流程的东西。这些数据更像是数据仓库,而不是在线商店 OLTP db。
  • 这也许是可能的。 Apex 有一个有用的会话和基于用户的对象,称为APEX_COLLECTIONS,它是一个包含多个通用 varchar 和数字类型列的表...c01、c02、c03、n01、n02、n03 等。我见过其他开发人员使用这个对象作为多个记录输出的存储桶。稍作调整,您可以通过使用过程调用填充此集合对象来测试这一点,然后使用列别名修改选择以生成输出。通用名称表明此持久对象是可重用的。愿这些讨论中的一些为您开辟更多选择!
猜你喜欢
  • 1970-01-01
  • 2010-12-11
  • 2015-04-07
  • 2021-01-05
  • 2018-10-24
  • 2010-11-02
  • 1970-01-01
  • 2012-01-09
  • 1970-01-01
相关资源
最近更新 更多