为 APEX 报告区域调整 Oracle REF CURSOR 数据
以下解决方案是在 apex.oracle.com 上的 Oracle Apex 演示托管实例上开发的;完成此操作时,托管的 Apex 发布版本为:4.2.5
根据您正在查看的 Oracle 版本,REF CURSOR 逻辑的实现有一些小的变化。我从 Oracle-Base 找到了一个很好的讨论。显着的变化包括:
- 创建
SYS_REFCURSOR 类型,这样开发人员就无需单独声明自己的自定义类型以在其代码中引用REF CURSOR 类型。
-
Microsoft ADO 或 Java 等编程语言/平台的语法和结构能够打开和迭代存储在
REF CURSOR 中的记录。这可能就是为什么有很多像你这样的商店在REF CURSOR 数据结构中使用程序代码的原因;它们简单且与其他编程语言兼容。
- 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 页面区域报告的一些额外更改:
- 添加了两种新的 SQL 对象类型:
EMPLOYEE_RECORD_TYPE 和 EMP_OUTPUT_TABLE_TYPE。
- 更改为 PL/SQL
FUNCTION 对象类型而不是 PROCEDURE 对象类型。
- 将输出数据类型更改为
NESTED TABLE 集合类型。 (这允许我们使用直接 SQL 查询存储在此集合中的输出数据)。
-
REF CURSOR 的分隔代码和报告输出查询已合并。
- 将用于测试第一部分的匿名 PL/SQL 块转换为
FUNCTION 对象。
- 从 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。一些需要考虑的附带想法:
- 在将数据从引用游标加载到 Oracle 集合类型时使用批量绑定操作和方法。
- 额外的模块化。您可以看到 OP 过程
my_proc 的组件在此解决方案的开发过程中发生了一些变化。您的转换工作可能会受益于使用包和流程分离(在有意义的情况下)更好地组织。
- 阅读有关 Oracle PL/SQL 集合的文档。您将了解一些重要的区别:
一种。有些集合类型可以直接用 SQL 查询。
湾。 PL/SQL 中定义的复合数据类型与架构级别定义的复合数据类型各有其局限性...
一般来说,明智地选择,或者只是坚持这个例子,因为它应该让你在转换现有的 PL/SQL 代码库时使用REF CURSOR 驱动的参数/输出。
前进!