我想记录Oracle中某些SELECT存储过程的运行时间。我将其分解为以下步骤。
重要说明:SELECT语句需要几分钟才能运行。
这是怎么回事:
在整个过程完成之前,不应更新LOG表。
基本上发生的是该过程立即插入,然后在SELECT语句完成的“之前”更新LOG表。
我尝试包装和嵌套其他BEGIN和END语句。在SELECT语句返回之前,存储过程仍在过程结束时运行“ UPDATE”语句。
CREATE OR REPLACE EDITIONABLE PROCEDURE SP_CUSTOMERDATA_GET (
PARAM_USERID IN VARCHAR2,
PARAM_FIRSTNAME IN VARCHAR2,
PARAM_LASTNAME IN VARCHAR2
OUTPUT OUT types.cursor_type)
AS
BEGIN
DECLARE
l_Id Number;
l_StartTime TIMESTAMP;
l_EndTime TIMESTAMP;
l_TotalTime Number;
BEGIN
l_StartTime:= systimestamp;
INSERT INTO PROC_LOG (SPNAME, PARM1, PARM2, PARM3)
VALUES ('SP_CUSTOMERDATA_GET',I_USERNAME, PARAM_USERID, PARAM_FIRSTNAME, PARAM_LASTNAME)
RETURNING ID INTO l_Id;
COMMIT;
OPEN OUTPUT FOR
SELECT *
FROM CUSTOMER
WHERE USERID=PARAM_USERID
AND FIRSTNAME=PARAM_FIRSTNAME
AND LASTNAME=PARAM_LASTNAME;
l_EndTime:= systimestamp;
l_TotalTime:= extract(second from (l_EndTime-l_StartTime));
--ISSUE: This statement runs before the SELECT statement above completes
UPDATE PROC_LOG
SET RUNTIME_SECONDS=l_TotalTime
WHERE ID=l_Id;
COMMIT;
END;
END SP_CUSTOMERDATA_GET;
我可以在PROC中设置一个属性,以强制该过程在上一个命令完成之前不运行下一个命令。程序没有按顺序运行没有意义吗?
答案 0 :(得分:1)
问题在于您的过程实际上并未运行SELECT
语句。您的过程仅打开分析该语句并获取该语句句柄的游标。它不会导致数据库实际执行该语句。当调用者从返回的游标中获取数据时,就会发生这种情况。该过程完成后,将不知道调用方是否要从游标中获取数据,是否仅要获取前10行,还是最终是否要获取每一行。如果您的目标是测量从游标中获取数据所花费的时间,则希望将日志记录添加到调用方,而不是此过程。
当然,您也可以单独运行SELECT
语句。如果实际查询与您发布的内容接近,我强烈建议您缺少customer
上的索引。我猜想userID
是唯一的,因此如果userID
上有一个索引,该查询应该在几毫秒内运行。
答案 1 :(得分:1)
我重新阅读了问题和贾斯汀的评论,并根据他的建议提出了代码解决方案。 首先是数据库结构的常规设置:
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
现在,原始过程将调用传递ref游标的函数,然后将此游标在其参数中转发给客户端应用程序:
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.