반응형

오늘 불현듯...

User Define Cursor 에 대한 부분이 궁금해 졌다...

자바에서 오라클 패키지 호출하여 커서값을 반환하여 자유 스럽게 사용했는데

그 커서 값을 오라클에서 받아서 해볼까 하는...

사실 cursor is 구문이라던지 직접 사용했지

그동안 cursor 반환은 무관심 했었다..

방법은...

즉,  커서 out 값에  l_cursor에 받아와서

3개의 select 한 값을 return 하는데... 각 값들의 속성을 l_ename, l_empno, l_deptno 로 fetch 하여 받아가지고 옴...

참고용

SET SERVEROUTPUT ON SIZE 1000000
DECLARE
  TYPE REF_CUR IS REF CURSOR;
  l_cursor REF_CUR;
  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;
/


출처 :  http://www.oracle-base.com/articles/misc/UsingRefCursorsToReturnRecordsets.php 




Using Ref Cursors To Return Recordsets

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.

import java.sql.*;
import oracle.jdbc.*;

public class TestResultSet  {
  public TestResultSet() {
    try {
      DriverManager.registerDriver (new oracle.jdbc.OracleDriver());
      Connection conn = DriverManager.getConnection("jdbc:oracle:oci:@w2k1", "scott", "tiger");
      CallableStatement stmt = conn.prepareCall("BEGIN get_emp_rs(?, ?); END;");
      stmt.setInt(1, 30); // DEPTNO
      stmt.registerOutParameter(2, OracleTypes.CURSOR); //REF CURSOR
      stmt.execute();
      ResultSet rs = ((OracleCallableStatement)stmt).getCursor(2);
      while (rs.next()) {
        System.out.println(rs.getString("ename") + ":" + rs.getString("empno") + ":" + rs.getString("deptno")); 
      }
      rs.close();
      rs = null;
      stmt.close();
      stmt = null;
      conn.close();
      conn = null;
    }
    catch (SQLException e) {
      System.out.println(e.getLocalizedMessage());
    }
  }

  public static void main (String[] args) {
    new TestResultSet();
  }
}
If you are using a version of Oracle before 9i, then create the following package and replace any references to SYS_REFCURSOR with TYPES.cursor_type.
CREATE OR REPLACE PACKAGE types AS 
  TYPE cursor_type IS REF CURSOR;
END Types; 
/
Hope this helps. Regards Tim... 
반응형

'Database > ORACLE' 카테고리의 다른 글

Automatic SQL Tuning in Oracle Database 10g  (0) 2012.07.20
10G OCP 자격증 관련  (2) 2012.07.08
일정 시간 별로 쿼리 정보 조회  (0) 2012.02.20
오라클 병렬 인덱스 조회 및 수정하기  (0) 2012.01.27
AWR Report 정리본  (0) 2011.09.28
반응형

Implicit Cursor가 Explicit Cursor에 비해 성능이 뛰어나다는 언급을 여러 번 봤을 것이다.

실제로도 그렇다.
하지만 왜 그런가?

간단한 테스트로 많은 것을 알 수 있다.

우선 다음과 같이 필요한 Object를 생성한다.

  1. @ukja102   
  2.   
  3. drop table t1 purge;   
  4. create table t1(c1 int, c2 char(10));   
  5. insert into t1   
  6. select level'dummy'  
  7. from dual   
  8. connect by level <= 200000;   
  9. commit;   
  10. select count(*) from t1;  

Implicit Cursor를 사용하는 경우와 Exlicit Cursor를 사용하는 경우의 성능을 비교해 보자.

  1. -- Implicit Cursor를 사용하는 경우   
  2. @ukja102   
  3. @mysid   
  4. @mon_on &v_sid   
  5.   
  6. begin  
  7.   for r in (select * from t1) loop   
  8.     null;   
  9.   end loop;   
  10. end;   
  11. /   
  12.   
  13. @mon_off   
  14. spool result1.txt   
  15. @mon_show   
  16. spool off  
  17.   
  18.   
  19. @ukja102   
  20. @mysid   
  21. @mon_on &v_sid   
  22.   
  23. -- Explicit Cursor를 사용하는 경우   
  24. declare  
  25.   cursor v_cursor is  
  26.     select * from t1   
  27.   ;   
  28.      
  29.   v_rec v_cursor%rowtype;   
  30. begin  
  31.   open v_cursor;   
  32.   loop   
  33.     fetch v_cursor into v_rec;   
  34.     exit when v_cursor%notfound;   
  35.   end loop;   
  36.   close v_cursor;   
  37. end;   
  38. /   
  39.   
  40. @mon_off   
  41. spool result2.txt   
  42. @mon_show   
  43. spool off  
여기서 한가지 질문을 던진다.

성능을 어떻게 비교할 것인가?

불행하게도 많은 사람들이 시작시간과 끝시간을 재는 것으로 만족한다. 그러지 말자.

Oracle은 성능을 비교하기 위한 많은 뷰들을 제공한다. 이들을 잘 활용해야 한다.

우선 v$sess_time_model 뷰를 통해 Time 정보를 비교한다. 이 뷰를 이용하면 별도의 코드를 통해 시간을 측정하지 않아도 된다.

  1. -- Implicit Cursor를 사용한 경우   
  2. STAT_NAME                                      VALUE1       VALUE2         DIFF   
  3. ---------------------------------------- ------------ ------------ ------------   
  4. DB time                                        59,773    1,777,125    1,717,352   
  5. sql execute elapsed time                       40,140    1,721,534    1,681,394   
  6. DB CPU                                         51,929    1,683,972    1,632,043   
  7. parse time elapsed                             42,324      256,573      214,249   
  8.   
  9. -- Explicit Cursor를 사용한 경우   
  10. STAT_NAME                                      VALUE1       VALUE2         DIFF   
  11. ---------------------------------------- ------------ ------------ ------------   
  12. DB time                                        29,622    6,051,808    6,022,186   
  13. sql execute elapsed time                       25,827    6,044,618    6,018,791   
  14. DB CPU                                         29,331    6,034,029    6,004,698   
  15. PL/SQL execution elapsed time                      60      558,753      558,693   
  16. parse time elapsed                              1,509      131,440      129,931  
Implicit Cursor가 모든 면에서 Explicit Cursor에 비해 현격한 성능 우위를 보이는 것을 알 수 있다.

그 이유가 무엇인지 가장 쉽게 알 수 있는 방법은?
Statistics을 봐야 한다. v$sesstat 뷰를 통해 본 차이는 다음과 같다.

  1. -- Implicit Cursor인 경우                                                                                  
  2. NAME                                           VALUE1       VALUE2         DIFF   
  3. ---------------------------------------- ------------ ------------ ------------   
  4. table scan rows gotten                             62      914,002      913,940   
  5. session pga memory max                      1,826,388    2,154,068      327,680   
  6. session uga memory max                      1,282,300    1,544,264      261,964   
  7. session pga memory                          1,826,388    1,957,460      131,072   
  8. session logical reads                             275        3,249        2,974   
  9.   
  10. -- Explicit Cursor인 경우   
  11. NAME                                           VALUE1       VALUE2         DIFF   
  12. ---------------------------------------- ------------ ------------ ------------   
  13. table scan rows gotten                             62   69,366,045   69,365,983   
  14. session pga memory max                      1,498,708    1,891,924      393,216   
  15. session pga memory                          1,498,708    1,891,924      393,216   
  16. session uga memory max                      1,151,372    1,413,336      261,964   
  17. 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을 사용하는 것이다. 아래와 같이...

  1. @ukja102   
  2. @mysid   
  3. @mon_on &v_sid   
  4.   
  5. declare  
  6.   cursor v_cursor is  
  7.     select * from t1   
  8.   ;   
  9.      
  10.   type c1tab is table of t1.c1%type;   
  11.   type c2tab is table of t2.c2%type;   
  12.      
  13.   c1t c1tab;   
  14.   c2t c2tab;   
  15.      
  16. begin  
  17.   open v_cursor;   
  18.   fetch v_cursor bulk collect into c1t, c2t; -- Do it bulk!!!   
  19.   close v_cursor;   
  20. end;   
  21. /   
  22.   
  23. @mon_off   
  24. spool result3.txt   
  25. @mon_show   
  26. spool off  
결과는 다음과 같다.
  1. -- Implicit Cursor를 사용한 경우   
  2. STAT_NAME                                      VALUE1       VALUE2         DIFF   
  3. ---------------------------------------- ------------ ------------ ------------   
  4. DB time                                        59,773    1,777,125    1,717,352   
  5. sql execute elapsed time                       40,140    1,721,534    1,681,394   
  6. DB CPU                                         51,929    1,683,972    1,632,043   
  7. parse time elapsed                             42,324      256,573      214,249   
  8.   
  9. -- Explicit Cursor + Bulk Collection을 사용한 경우   
  10. STAT_NAME                                      VALUE1       VALUE2         DIFF   
  11. ---------------------------------------- ------------ ------------ ------------   
  12. DB time                                        28,024    1,503,542    1,475,518   
  13. DB CPU                                         18,620    1,489,167    1,470,547   
  14. sql execute elapsed time                       24,547    1,493,775    1,469,228   
  15. PL/SQL execution elapsed time                      59        5,512        5,453   
  16. parse time elapsed                              1,302        4,793        3,491  
Bulk Collection과 함께 Explicit Cursor를 사용한 경우 오히려 성능이 더 뛰어나다. 그 이유는?
  1. -- Implicit Cursor인 경우                                                                                  
  2. NAME                                           VALUE1       VALUE2         DIFF   
  3. ---------------------------------------- ------------ ------------ ------------   
  4. table scan rows gotten                             62      914,002      913,940   
  5. session pga memory max                      1,826,388    2,154,068      327,680   
  6. session uga memory max                      1,282,300    1,544,264      261,964   
  7. session pga memory                          1,826,388    1,957,460      131,072   
  8. session logical reads                             275        3,249        2,974   
  9.   
  10. -- Explicit Cursor + Bulk Collection인 경우   
  11. NAME                                           VALUE1       VALUE2         DIFF   
  12. ---------------------------------------- ------------ ------------ ------------   
  13. session pga memory max                      1,498,708   21,618,260   20,119,552   
  14. session uga memory max                      1,151,372    1,478,800      327,428   
  15. table scan rows gotten                             62      200,062      200,000  
Bulk Collection을 사용한 경우 한번에 필요한 Row를 Fetch하기 때문에 일량은 현격하게 주는 반면에 많은 양의 메모리(20M)를 사용한다. 그만큼 성능은 개선되었지만 그 대가는 메모리가 되는 셈이다.

위의 테스트 결과는 많은 것을 말해 준다.

  • 왜 성능 차이가 나는지는 알아야 하며, 또 알 수 있다.
  • 성능의 개선에는 대가가 따르며, 그 대가가 무엇인지도 알 수 있다.
  • 성능을 측정하는 다양한 뷰를 잊지 말라. 단순히 시간이나 일량만 보지 말라.
  • 자동화하라. Toad나 Orange같은 툴을 사용하지 말고 SQL*Plus를 이용해 [Enter]한번으로 결과가 나오게끔 하라

테스트에 사용한 스크립트는 아래에서 볼 수 있다.

<script language=javascript src="http://wiki.ex-em.com/hilite/Scripts/shCore.js">크리에이티브 커먼즈 라이선스
Creative Commons License

 

 

출처 : http://ukja.tistory.com/156

반응형

+ Recent posts