Since Oracle 7.3 the REF CURSOR type has been available to allow recordsets to be returned from stored procedures and functions. Oracle 9i introduced the predefined SYS_REFCURSOR type, meaning we no longer have to define our own REF CURSOR types. The example below uses a ref cursor to return a subset of the records in the EMP table.
The following procedure opens a query using a SYS_REFCURSOR output parameter. Notice the cursor is not closed in the procedure. It is up to the calling code to manage the cursor once it has been opened.
CREATE OR REPLACE
PROCEDURE get_emp_rs (p_deptno IN emp.deptno%TYPE,
p_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN p_recordset FOR
SELECT ename,
empno,
deptno
FROM emp
WHERE deptno = p_deptno
ORDER BY ename;
END GetEmpRS;
/
The resulting cursor can be referenced from PL/SQL as follows.
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
l_cursor SYS_REFCURSOR;
l_ename emp.ename%TYPE;
l_empno emp.empno%TYPE;
l_deptno emp.deptno%TYPE;
BEGIN
get_emp_rs (p_deptno => 30,
p_recordset => l_cursor);
LOOP
FETCH l_cursor
INTO l_ename, l_empno, l_deptno;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(l_ename || ' | ' || l_empno || ' | ' || l_deptno);
END LOOP;
CLOSE l_cursor;
END;
/
The cursor can be used as an ADO Recordset.
Dim conn, cmd, rs
Set conn = Server.CreateObject("adodb.connection")
conn.Open "DSN=TSH1;UID=scott;PWD=tiger"
Set cmd = Server.CreateObject ("ADODB.Command")
Set cmd.ActiveConnection = conn
cmd.CommandText = "get_emp_rs"
cmd.CommandType = 4 'adCmdStoredProc
Dim param1
Set param1 = cmd.CreateParameter ("deptno", adInteger, adParamInput)
cmd.Parameters.Append param1
param1.Value = 30
Set rs = cmd.Execute
Do Until rs.BOF Or rs.EOF
-- Do something
rs.MoveNext
Loop
rs.Close
conn.Close
Set rs = nothing
Set param1 = nothing
Set cmd = nothing
Set conn = nothing
The cursor can also be referenced as a Java ResultSet.
-- Implicit Cursor를 사용하는 경우
@ukja102
@mysid
@mon_on &v_sid
begin
for r in (select * from t1) loop
null;
end loop;
end;
/
@mon_off
spool result1.txt
@mon_show
spool off
@ukja102
@mysid
@mon_on &v_sid
-- Explicit Cursor를 사용하는 경우
declare
cursor v_cursor is
select * from t1
;
v_rec v_cursor%rowtype;
begin
open v_cursor;
loop
fetch v_cursor into v_rec;
exit when v_cursor%notfound;
end loop;
close v_cursor;
end;
/
@mon_off
spool result2.txt
@mon_show
spool off
여기서 한가지 질문을 던진다.
성능을 어떻게 비교할 것인가?
불행하게도 많은 사람들이 시작시간과 끝시간을 재는 것으로 만족한다. 그러지 말자.
Oracle은 성능을 비교하기 위한 많은 뷰들을 제공한다. 이들을 잘 활용해야 한다.
우선 v$sess_time_model 뷰를 통해 Time 정보를 비교한다. 이 뷰를 이용하면 별도의 코드를 통해 시간을 측정하지 않아도 된다.
sql execute elapsed time 25,827 6,044,618 6,018,791
DB CPU 29,331 6,034,029 6,004,698
PL/SQL execution elapsed time 60 558,753 558,693
parse time elapsed 1,509 131,440 129,931
-- Implicit Cursor를 사용한 경우
STAT_NAME VALUE1 VALUE2 DIFF
---------------------------------------- ------------ ------------ ------------
DB time 59,773 1,777,125 1,717,352
sql execute elapsed time 40,140 1,721,534 1,681,394
DB CPU 51,929 1,683,972 1,632,043
parse time elapsed 42,324 256,573 214,249
-- Explicit Cursor를 사용한 경우
STAT_NAME VALUE1 VALUE2 DIFF
---------------------------------------- ------------ ------------ ------------
DB time 29,622 6,051,808 6,022,186
sql execute elapsed time 25,827 6,044,618 6,018,791
DB CPU 29,331 6,034,029 6,004,698
PL/SQL execution elapsed time 60 558,753 558,693
parse time elapsed 1,509 131,440 129,931
Implicit Cursor가 모든 면에서 Explicit Cursor에 비해 현격한 성능 우위를 보이는 것을 알 수 있다.
그 이유가 무엇인지 가장 쉽게 알 수 있는 방법은? Statistics을 봐야 한다. v$sesstat 뷰를 통해 본 차이는 다음과 같다.
session pga memory max 1,498,708 1,891,924 393,216
session pga memory 1,498,708 1,891,924 393,216
session uga memory max 1,151,372 1,413,336 261,964
session logical reads 72 200,261 200,189
-- Implicit Cursor인 경우
NAME VALUE1 VALUE2 DIFF
---------------------------------------- ------------ ------------ ------------
table scan rows gotten 62 914,002 913,940
session pga memory max 1,826,388 2,154,068 327,680
session uga memory max 1,282,300 1,544,264 261,964
session pga memory 1,826,388 1,957,460 131,072
session logical reads 275 3,249 2,974
-- Explicit Cursor인 경우
NAME VALUE1 VALUE2 DIFF
---------------------------------------- ------------ ------------ ------------
table scan rows gotten 62 69,366,045 69,365,983
session pga memory max 1,498,708 1,891,924 393,216
session pga memory 1,498,708 1,891,924 393,216
session uga memory max 1,151,372 1,413,336 261,964
session logical reads 72 200,261 200,189
차이가 무엇인가?
놀랍게도 일량(Reads)의 차이가 절대적이라는것을 알 수 있다. logical reads가 10배 정도 차이나며 그 차이로 인해 성능의 차이가 왔다.
이 차이는 어디서 온 것인가?
Fetch Array Size에서 온 것이다. 한번에 많은 로우를 Fetch하면 Block을 방문해야할횟수가 줄어들며 그만큼 Logical Reads가 줄어든다. Implicit Cursor를 사용하는 경우에 Oracle은 내부적으로 10개를 한번에 Fetch한다. 반면에 Explicit Cursor를 사용하는 경우에는 한번에 한 개의 Row만 Fetch한다. 그 결과로 Logical Reads가 대략 10배의 차이가 나게 된다. 그 만큼 성능이 느린 것이다.
Explicit Cursor를 Implicit Cursor보다 빠르게 하는 유일한 방법은 Bulk Collection을 사용하는 것이다. 아래와 같이...
fetch v_cursor bulk collect into c1t, c2t; -- Do it bulk!!!
close v_cursor;
end;
/
@mon_off
spool result3.txt
@mon_show
spool off
@ukja102
@mysid
@mon_on &v_sid
declare
cursor v_cursor is
select * from t1
;
type c1tab is table of t1.c1%type;
type c2tab is table of t2.c2%type;
c1t c1tab;
c2t c2tab;
begin
open v_cursor;
fetch v_cursor bulk collect into c1t, c2t; -- Do it bulk!!!
close v_cursor;
end;
/
@mon_off
spool result3.txt
@mon_show
spool off
sql execute elapsed time 24,547 1,493,775 1,469,228
PL/SQL execution elapsed time 59 5,512 5,453
parse time elapsed 1,302 4,793 3,491
-- Implicit Cursor를 사용한 경우
STAT_NAME VALUE1 VALUE2 DIFF
---------------------------------------- ------------ ------------ ------------
DB time 59,773 1,777,125 1,717,352
sql execute elapsed time 40,140 1,721,534 1,681,394
DB CPU 51,929 1,683,972 1,632,043
parse time elapsed 42,324 256,573 214,249
-- Explicit Cursor + Bulk Collection을 사용한 경우
STAT_NAME VALUE1 VALUE2 DIFF
---------------------------------------- ------------ ------------ ------------
DB time 28,024 1,503,542 1,475,518
DB CPU 18,620 1,489,167 1,470,547
sql execute elapsed time 24,547 1,493,775 1,469,228
PL/SQL execution elapsed time 59 5,512 5,453
parse time elapsed 1,302 4,793 3,491
Bulk Collection과 함께 Explicit Cursor를 사용한 경우 오히려 성능이 더 뛰어나다. 그 이유는?