Database/Oracle2011. 5. 26. 14:15

 커서란 SQL*Plus에서 사용자가 실행한 SQL문의 단위를 의미합니다. 오라클에서 수행한 모든 쿼리문은 커서 단위로 처리합니다. 커서는 암시적 커서와 명시적 커서로 나뉩니다. PL/SQL의 SQL문처럼 하나의 결과를 리턴하는 경우 커서 없이도 SQL문의 실행된 결과가 암시적으로 커서에 저장되므로 이를 암시적 커서라고 합니다.

- SQL문을 수행한 후에 결과로 얻어지는 행이 여러 개일 경우에는 암시적인 커서에 정보를 저장할 수 없기에 에러가 발생합니다. 이런 경우에 반드시 명시적인 커서를 사용해야 합니다.

# 커서의 상태
사용자 삽입 이미지







- 명시적인 커서는 PL/SQL의 레코드(RECORD)와 PL/SQL 의 테이블(TABLE)을 결합한 것으로서 프로그램 언어의 구조체 배열과 유사합니다. 커서를 사용하기 위해서는 다음과 같은 4가지 단계를 거쳐야 합니다.
1. 커서를 선언한다. (CURSOR cur_name)
2. 커서를 오픈한다. (OPEN cur_name)
3. 커서의 조회한 결과를 인출해 저장한다. (FETCH cur_name)
4. 커서를 닫는다. (CLOSE cur_name)

1. 커서를 선언한다.
CURSOR cursor_name
IS statement;

- 커서를 선언할 때에는 실행할 SELECT 문을 IS 다음에 서브쿼리 형태로 기술합니다.
CURSOR C1 IS
SELECT EMPNO, ENAME, SAL
FROM EMP
WHERE DEPTNO = 20;
- C1은 커서의 이름이고 이 커서는 사원 테이블에서 부서번호가 20인 행을 조회합니다.

2. 커서를 오픈한다.
- 커서를 오픈 하게 되면 커서를 선언할 때 작성한 SELECT 문을 실행하게 됩니다.
OPEN C1;

3. 커서에 조회한 결과를 인출해 저장한다.
- FETCH 명령어를 수행하면 오픈한 SELECT문에 의해 검색된 한 개의 정보를 읽어오게 됩니다.
읽어온 정보는 변수에 저장합니다.
FETCH C1 INTO VEMPNO, VENAME, VSAL;

- SELECT 문의 결과로 얻어지는 행이 여러개일 경우에는  LOOP-END LOOP와 같은 반복문을 이용해서 마지막 행을 읽을 때가지 반복해서 수행해야 합니다.
LOOP
    FETCH C1 INTO VEMPNO, VENAME, VSAL;
    EXIT WHEN C1%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(TO_CHAR(VEMPNO) || VNAME || TO_CHAR(VSAL));
END LOOP;

- 만족하는 행을 발견하지 못할 때 즉, 마지막 행을 읽었을 때 반복문을 벗어나도록 하기 위해서는 'EXIT WHEN C1%NOTFOUND'와 같이 기술 합니다.

4. 커서를 닫는다.
- CLOSE 명령문으로 더 이상 사용되지 않을 커서를 닫습니다.
CLOSE C1;
- 커서를 닫으면 (CLOSE) 커서에 선언되 SELECT문의 선언이 해제됩니다.

- 20번 부서에 근무하는 사원의 정보를 출력해 봅니다.
SQL> SET SERVEROUTPUT ON -- DBMS_OUTPUT 출력 가능하도록 설정.
SQL> DECLARE
-- 커서를 실행한 결과를 저장할 지역 변수 선언
  2     vempno   NUMBER(4);
  3     vename   VARCHAR2(20);
  4     vsal    NUMBER(7, 2);
  5 
-- 번호가 20인 사원의 정보를 조회하는 SELECT문을 커서로 선언.
  6     CURSOR C1
  7     IS
  8     SELECT EMPNO, ENAME, SAL
  9     FROM EM01
 10     WHERE DEPTNO = 20;
 11 
 12  BEGIN
 13     OPEN C1; -- 커서를 오픈하면 커서를 선언할 때 작성한 SELECT 문이 실행됩.
 14     dbms_output.put_line('번호   이름   급여');
 15 
 16     LOOP
 17             FETCH C1 INTO VEMPNO, VENAME, VSAL; -- SELECT 문을 실행한 결과를 한행씩 가져와서 변수에 저장
 18             EXIT WHEN C1%NOTFOUND; -- 마지막 행까지 읽어서 행이 발견되지 않으면 반복문을 벗어남.
 19             DBMS_OUTPUT.PUT_LINE(TO_CHAR(VEMPNO) || '       ' || VENAME || '         ' || TO_CHAR(VSAL)); -- 검색된 결과를 사용자 화면에 출력.
        END LOOP ;
-- LOOP 구문 : 커서 실행 결과 구해진 행이 여러 개이면 반복문을 사용하여 처리.
 20   21        CLOSE C1;
 22  END ;
 23  /
번호   이름   급여
7566    JONES    2975
7902    FORD     3000
7368    SMITH    800
7788    SCOTT    3000
7876    TEST3    1100

PL/SQL procedure successfully completed.

SQL>

# OPEN-FETCH-CLOSE가 없이 커서 처리
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2  -- 커서를 수행한 결과 값을 저장할 레코드 형태의 변수.
  3     vemp     emp%ROWTYPE;
  4 
  5  -- 커서 선언
  6     CURSOR C1
  7     IS
  8     SELECT EMPNO, ENAME, SAL
  9     FROM EM01
 10     WHERE DEPTNO = 20;
 11 
 12  BEGIN
 13     dbms_output.put_line('번호   이름   급여');
 14 
 15  /* FOR 레코드명 IN 커서명 LOOP */
 16  -- OPEN-FETCH-CLOSE문을 하나의 FOR문으로 대체할 수 있음.
 17 
 18     FOR vemp IN C1 LOOP
 19             EXIT WHEN C1%NOTFOUND;
 20             DBMS_OUTPUT.PUT_LINE(TO_CHAR(vemp.empno) || '   ' || vemp.ename || '    ' || TO_CHAR(vemp.sal) || '------------->'||C1%ROWCOUNT);
 21     END LOOP;
 22  END ;
 23  /
번호   이름   급여
7566    JONES   2975------------->1
7902    FORD    3000------------->2
7368    SMITH   800------------->3
7788    SCOTT   3000------------->4
7876    TEST3   1100------------->5

PL/SQL procedure successfully completed.

# 커서를 활용한 실용 예제
SQL> DECLARE
  2     tot      NUMBER := 0;
  3     CURSOR emp_cursor
  4     IS
  5     SELECT ENAME, SAL
  6     FROM EM01;
  7  BEGIN
  8     dbms_output.put_line('이름                          급여');
  9     dbms_output.put_line('-------------------------');
 10 
 11     FOR cur_var IN emp_cursor LOOP
 12             tot := tot + cur_var.sal;
 13             dbms_output.put_line(cur_var.ename);
 14             dbms_output.put_line('_                ' || cur_var.sal);
 15     END LOOP;
 16     dbms_output.put_line('-------------------------');
 17     dbms_output.put_line('_                            ' || tot);
 18  END;
 19  /
이름                          급여
-------------------------
MARTIN
_                1250
JONES
_                2975
TURNER
_                1500
FORD
_                3000
SMITH
_                800
KING
_                5000
WARD
_                1250
BLAKE
_                2850
CLARK
_                2450
MILLER
_                1300
ALLEN
_                1600
SCOTT
_                3000
JAMES
_                1957
TEST
_                800
TEST3
_                1100
-------------------------
_                            30832

PL/SQL procedure successfully completed.

- 사원별 급여 현황을 그래프로 표현
SQL> DECLARE
  2     CURSOR emp_cursor
  3     IS
  4     SELECT ENAME, SAL
  5     FROM EMP
  6     ORDER BY SAL DESC;
  7 
  8     star    VARCHAR2(100);
  9     cnt     NUMBER := 0;
 10  BEGIN
 11     dbms_output.put_line('                       사원별 급여 현황');
 12     dbms_output.put_line('----------------------------------------');
 13 
 14     FOR cur_var IN emp_cursor LOOP
 15             star := NULL;
 16             cnt := round(cur_var.sal/100, 0);
 17 
 18             FOR i IN 1.. cnt LOOP
 19                     star := star || '*';
 20             END LOOP;
 21 
 22     dbms_output.put_line(cur_var.ename);
 23     dbms_output.put_line('_               ' || star || '    ' || ' (' || cur_var.sal || ')');
 24     END LOOP;
 25  END;
 26  /
사원별 급여 현황
----------------------------------------
KING
_               **************************************************       (5000)
FORD
_               ******************************   (3000)
SCOTT
_               ******************************   (3000)
JONES
_               ******************************   (2975)
BLAKE
_               *****************************    (2850)
CLARK
_               *************************        (2450)
JAMES
_               ********************     (1957)
ALLEN
_               ****************         (1600)
TURNER
_               ***************  (1500)
MILLER
_               *************    (1300)
MARTIN
_               *************    (1250)
WARD
_               *************    (1250)
ADAMS
_               ***********      (1100)
SMITH
_               ********         (800)
SMITH
_               ********         (800)

PL/SQL procedure successfully completed.

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

롤백 세그먼트(rollback segment)란 ?  (0) 2011.06.25
Oracle Lock  (0) 2011.06.22
아카이빙(archiving)이란?  (0) 2011.06.22
Oracle에서 데이터 문자열을 구분자로 나누기  (0) 2011.06.14
Oracle Cursor  (0) 2011.05.26
Posted by 아로나