假设我有这样的程序
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
答案 0 :(得分:2)
以下解决方案是在apex.oracle.com上的Oracle Apex演示托管实例上开发的。在完成此操作时,托管的Apex发行版本为:4.2.5
根据您所查看的Oracle版本,REF CURSOR
逻辑的实现在很小程度上发生了变化。我在Oracle-Base中找到了一个很好的讨论。值得注意的变化包括:
SYS_REFCURSOR
类型,无需开发人员独立声明自己的自定义类型,以便在代码中引用REF CURSOR
类型。REF CURSOR
中的记录的语法和结构。这可能就是为什么有很多像你这样的商店在REF CURSOR
数据结构中有程序代码;它们很简单,并且与其他编程语言兼容。SELECT
查询定义的数据输入 即可。 以下是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。
REF CURSOR
数据进一步准备原始样本程序以用于Apex页面区域报告的一些其他更改:
EMPLOYEE_RECORD_TYPE
和EMP_OUTPUT_TABLE_TYPE
。FUNCTION
对象类型而非PROCEDURE
对象类型。NESTED TABLE
集合类型。 (这允许我们使用直接SQL来查询存储在此集合中的输出数据。)REF CURSOR
和报告输出查询的分隔代码。FUNCTION
对象。以下是修订后的代码:
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,如区域定义配置页面的“Region Source”部分。
比较:源表数据与REF CURSOR查询结果
考虑到Oracle 9i之间RDBMS产品版本的飞跃,包括11g和12c,这个例子为大量优化留出了空间。一些需要考虑的想法:
my_proc
的组件移动了一点。您的转换工作可能会受益于更好的组织使用包和流程分离(有意义的地方)。一般情况下,明智地选择,或者只是坚持使用这个示例,因为它应该可以帮助您将现有的PL / SQL代码库转换为REF CURSOR
驱动的参数/输出。
冠!