我重新阅读了这个问题和 Justin 的 cmets,并根据他的建议提出了一个代码解决方案。
首先是数据库结构的一般设置:
FSITJA@db01>create table customer (userid,
2 firstname,
3 lastname) as
4 select level, 'John', 'Doe'
5 from dual
6 connect by level <= 1000000;
Table created.
FSITJA@db01>create table PROC_LOG (id number generated as identity,
2 SPNAME varchar2(30),
3 PARM1 varchar2(100),
4 PARM2 varchar2(100),
5 PARM3 varchar2(100),
6 RUNTIME_SECONDS number);
Table created.
FSITJA@db01>create or replace type tp_customer_row as object (userid number,
2 firstname varchar2(100),
3 lastname varchar2(100));
4 /
Type created.
FSITJA@db01>create or replace type tp_customer as table of tp_customer_row;
2 /
Type created.
FSITJA@db01>create or replace package types as
2 type cursor_type is ref cursor return customer%rowtype;
3 end;
4 /
Package created.
然后我们需要一个带有自治事务的存储过程来记录时间,以及允许我们从集合中查询数据的表函数。我们可以将光标传递到 Select 中的函数中以测试它是否有效:
FSITJA@db01>create or replace procedure sp_log_customerdata_get(proc_log_id in proc_log.id%type, starttime in timestamp) as
2 pragma autonomous_transaction;
3 begin
4 UPDATE PROC_LOG
5 SET RUNTIME_SECONDS=extract(second from (systimestamp-starttime))
6 WHERE ID=proc_log_id;
7 COMMIT;
8 end;
9 /
Procedure created.
FSITJA@db01>create or replace function fn_customerdata_get(cust_cursor types.cursor_type,
2 proc_log_id in proc_log.id%type,
3 starttime in timestamp) return tp_customer
4 pipelined as
5 in_cust_rec customer%rowtype;
6 out_cust_rec tp_customer_row := tp_customer_row(null, null, null);
7 begin
8 loop
9 fetch cust_cursor into in_cust_rec;
10 exit when cust_cursor%notfound;
11 out_cust_rec.userid := in_cust_rec.userid;
12 out_cust_rec.firstname := in_cust_rec.firstname;
13 out_cust_rec.lastname := in_cust_rec.lastname;
14 pipe row(out_cust_rec);
15 end loop;
16 close cust_cursor;
17 sp_log_customerdata_get(proc_log_id, starttime);
18 return;
19 end;
20 /
Function created.
FSITJA@db01>select *
2 from table(fn_customerdata_get(cursor(select userid,
3 firstname,
4 lastname
5 from customer
6 where rownum < 5),
7 null,
8 systimestamp));
USERID FIRSTNAME LASTNAME
---------- --------------- ---------------
1 John Doe
2 John Doe
3 John Doe
4 John Doe
现在是原始过程,它将调用传递引用游标的函数,然后将该游标转发到客户端应用程序的参数中:
FSITJA@db01>CREATE OR REPLACE PROCEDURE SP_CUSTOMERDATA_GET (
2 PARAM_USERID IN VARCHAR2,
3 PARAM_FIRSTNAME IN VARCHAR2,
4 PARAM_LASTNAME IN VARCHAR2,
5 OUTPUT OUT types.cursor_type) AS
6 l_Id Number;
7 l_StartTime TIMESTAMP;
8 l_EndTime TIMESTAMP;
9 l_TotalTime Number;
10 l_CustResult tp_customer;
11 BEGIN
12 l_StartTime:= systimestamp;
13 INSERT INTO PROC_LOG (SPNAME, PARM1, PARM2, PARM3)
14 VALUES ('SP_CUSTOMERDATA_GET', PARAM_USERID, PARAM_FIRSTNAME, PARAM_LASTNAME)
15 RETURNING ID INTO l_Id;
16 COMMIT;
17 open output for
18 select *
19 from table(fn_customerdata_get(cursor(SELECT userid,
20 firstname,
21 lastname
22 FROM CUSTOMER
23 WHERE USERID=PARAM_USERID
24 AND FIRSTNAME=PARAM_FIRSTNAME
25 AND LASTNAME=PARAM_LASTNAME),
26 l_Id,
27 l_StartTime
28 )
29 );
30 END SP_CUSTOMERDATA_GET;
31 /
Procedure created.
最后是一段代码来测试,只有在客户端应用程序从表函数中获取数据之后,才会有一个经过时间的日志条目:
FSITJA@db01>declare
2 v_output types.cursor_type;
3 v_runtime_seconds number;
4 type tp_cust_table is table of customer%rowtype;
5 v_cust_table tp_cust_table;
6 begin
7 SP_CUSTOMERDATA_GET (1, 'John', 'Doe', v_output);
8 select runtime_seconds
9 into v_runtime_seconds
10 from proc_log
11 where id = 1;
12 dbms_output.put_line('Runtime before client fetches: ' || v_runtime_seconds);
13 fetch v_output
14 bulk collect into v_cust_table;
15 select runtime_seconds
16 into v_runtime_seconds
17 from proc_log
18 where id = 1;
19 dbms_output.put_line('Runtime AFTER client fetches: ' || v_runtime_seconds);
20 end;
21 /
Runtime before client fetches:
Runtime AFTER client fetches: .118791
PL/SQL procedure successfully completed.