본문 바로가기
DATABASE/ORACLE

[PL/SQL] 무작정 시작하기 (4) - CURSOR

by 허도치 2020. 1. 14.

2020/01/13 - [DATABASE/ORACLE] - [PL/SQL] 무작정 시작하기 (1) - PL/SQL 이란?

2020/01/13 - [DATABASE/ORACLE] - [PL/SQL] 무작정 시작하기 (2) - IF 조건문

2020/01/13 - [DATABASE/ORACLE] - [PL/SQL] 무작성 시작하기 (3) - LOOP, WHILE 반복문

 

 

  지난 시간까지 조건문과 반복문에 대해서 알아보았다. 이번 포스트에서는 반복문과 짝꿍을 이뤄서 PL/SQL에서 가장 많이 사용되는 CURSOR에 대해서 알아보도록 하겠다. 

 

 

1. CURSOR란?

   1-1. SELECT문을 통해 얻어진 결과 행들은 메모리에 저장되는데, 이 저장되는 장소를 커서(CURSOR)라고 함.

 

   1-2. PL/SQL에서는 원래 단일행에 대한 처리만 가능하지만, CURSOR를 이용하면 복수개의 행을 처리할 수 있음.

 

   1-3. CURSOR에 있는 데이터를 가지고 오는것을 FETCH라고 함.

 

   1-4. CURSOR 선언 -> CURSOR OPEN -> FETCH -> CURSOR CLOSE 순으로 사용됨.

 

   1-5. 무시적 커서와 명시적 커서가 있음.

 

   1-6. 4개의 속성을 가지며, 커서의 종류별로 사용이 달라짐.

          - SQL%ISOPEN

          - SQL%FOUND

          - SQL%NOTFOUND

          - SQL%ROWCOUNT

 

 

2. 묵시적 커서란?

   2-1. DML이 실행되면 자동으로 생성. 관리되는 커서.

 

   2-2. 쿼리가 실행되는 순간 CURSOR의 OPEN과 CLOSE가 실행됨.

 

   2-3. 속성별 정의

          - SQL%ISOPEN: 쿼리가 실행되는 순간 CLOSE 되기 때문에 항상 FALSE.

          - SQL%FOUND: 조회된 결과가 1개 이상인 경우 TRUE.

          - SQL%NOTFOUND: 조회된 결과가 0개인 경우 TRUE.

          - SQL%ROWCOUNT: 최근 수행된 쿼리의 결과행 수.

 

3. 묵시적 커서 예제.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
--실행 결과를 출력하도록 설정
SET SERVEROUTPUT ON
 
--스크립트 경과 시간을 출력하도록 설정
SET TIMING ON
 
DECLARE
--변수를 정의하는 영역
 
    V_CNT           NUMBER := 1;
    
    V_ROWCOUNT      NUMBER;
    V_ISOPEN        VARCHAR2(8);
    V_FOUND         VARCHAR2(8);
    V_NOTFOUND      VARCHAR2(8);
 
BEGIN
--작업 영역
    
    SELECT COUNT(1)
      INTO V_CNT
      FROM SCOTT.EMP
    ;
    
    V_ROWCOUNT := SQL%ROWCOUNT;
    V_ISOPEN := CASE WHEN SQL%ISOPEN THEN 'TRUE' ELSE 'FALSE' END;
    V_FOUND := CASE WHEN SQL%FOUND THEN 'TRUE' ELSE 'FALSE' END;
    V_NOTFOUND := CASE WHEN SQL%NOTFOUND THEN 'TRUE' ELSE 'FALSE' END;
    
    DBMS_OUTPUT.PUT_LINE( V_ROWCOUNT );     --1
    DBMS_OUTPUT.PUT_LINE( V_ISOPEN );       --FALSE
    DBMS_OUTPUT.PUT_LINE( V_FOUND );        --TRUE
    DBMS_OUTPUT.PUT_LINE( V_NOTFOUND );     --FALSE
    
END
;
--작업 종료
cs

 

 

4. 명시적 커서란?

   4-1. 사용자가 선언하는 CURSOR.

 

   4-2. CURSOR 선언, CURSOR OPEN, FETCH, CURSOR CLOSE를 작성해야함.

 

   4-3. 속성별 정의

          - CURSOR%ISOPEN: CURSOR가 OPEN상태인 경우 TRUE.

          - CURSOR%FOUND: 조회된 결과가 1개 이상인 경우 TRUE.

          - CURSOR%NOTFOUND: 조회된 결과가 0개인 경우 TRUE.

          - CURSOR명%ROWCOUNT: 최근 수행된 쿼리의 결과행 수.

            * LOOP 내에서 사용할 경우, INDEX처럼 COUNT가 1씩 증가함.

 

   4-4. 명시적 커서를 정의 하는 방법.

           - CURSOR ~ IS: CURSOR를 선언하고 직접 OPEN, FETCH, CLOSE 하여 사용. 

           - IS REF CURSOR: CURSOR를 선언하고 직접 OPEN, FETCH, CLOSE 하여 사용, 쿼리를 동적으로 작성할 수 있음.

           - FOR ~ IN: CURSOR를 선언할 필요가 없으며,  CURSOR의 OPEN, CLOSE, FETCH가 자동으로 관리됨.

 

 

5. 명시적 커서 예제.

   5-1. CURSOR ~ IS 에제.

           - EMP 테이블에서 사원번호, 사원명, 직업, 급여를 조회하시오.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
--실행 결과를 출력하도록 설정
SET SERVEROUTPUT ON
 
--스크립트 경과 시간을 출력하도록 설정
SET TIMING ON
 
 
DECLARE
--변수를 정의하는 영역
 
    V_RESULT_MSG    VARCHAR2(500);
        
    CURSOR CUR1 IS (
        SELECT T1.EMPNO
             , T1.ENAME
             , T1.JOB
             , T1.SAL
          FROM SCOTT.EMP T1
    );
    
    V_EMPNO SCOTT.EMP.EMPNO%TYPE;
    V_ENAME SCOTT.EMP.ENAME%TYPE;
    V_JOB SCOTT.EMP.JOB%TYPE;
    V_SAL SCOTT.EMP.SAL%TYPE;
 
BEGIN
--작업 영역
    
    OPEN CUR1;
    
    LOOP
        
        FETCH CUR1 INTO V_EMPNO, V_ENAME, V_JOB, V_SAL;
        
        EXIT WHEN CUR1%NOTFOUND;
        
        V_RESULT_MSG := V_EMPNO||' / '||V_ENAME||' / '||V_JOB||' / '||V_SAL;
        
        DBMS_OUTPUT.PUT_LINE( V_RESULT_MSG );
    
    END LOOP;
    
    CLOSE CUR1;
 
END
;
--작업 종료
cs

           - 13~19 ln: EMP 테이블을 조회한 결과를 저장하는 CURSOR를 선언,

           - 21~24 ln: FETCH된 값을 저장할 변수, EMP 테이블의 컬럼을 참조하여 데이터의 타입을 정의, 

           - 29 ln: CURSOR를 OPEN하여 FETCH가 가능한 상태로 변경.

           - 31 ln: 전체 행을 조회하기 위해 반복문 실행.

           - 33 ln: CURSOR를 FETCH하여 한 행의 데이터를 추출하여 변수에 저장.

           - 35 ln: CURSOR에 더이상 FETCH할 행이 없는 경우 반복문 종료.

           - 37 ~39 ln: 결과값 출력.

           - 41 ln: 반복문 종료.

           - 43 ln: 커서 종료.

 

   5-2. IS REF CURSOR 에제.

           - EMP 테이블에서 사원번호, 사원명, 직업, 급여를 조회하시오.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
--실행 결과를 출력하도록 설정
SET SERVEROUTPUT ON
 
--스크립트 경과 시간을 출력하도록 설정
SET TIMING ON
 
 
DECLARE
--변수를 정의하는 영역
 
    V_SQL           VARCHAR2(500);
    V_RESULT_MSG    VARCHAR2(500);
    
    TYPE MY_CURSOR IS REF CURSOR;
    CUR1 MY_CURSOR;
    
    V_EMPNO SCOTT.EMP.EMPNO%TYPE;
    V_ENAME SCOTT.EMP.ENAME%TYPE;
    V_JOB SCOTT.EMP.JOB%TYPE;
    V_SAL SCOTT.EMP.SAL%TYPE;
 
BEGIN
--작업 영역
 
    V_SQL := '
        SELECT T1.EMPNO
             , T1.ENAME
             , T1.JOB
             , T1.SAL
          FROM SCOTT.EMP T1
    ';
 
    OPEN CUR1 FOR V_SQL;
    
    LOOP
        
        FETCH CUR1 INTO V_EMPNO, V_ENAME, V_JOB, V_SAL;
        
        EXIT WHEN CUR1%NOTFOUND;
        
        V_RESULT_MSG := V_EMPNO||' / '||V_ENAME||' / '||V_JOB||' / '||V_SAL;
        
        DBMS_OUTPUT.PUT_LINE( V_RESULT_MSG );
    
    END LOOP;
    
    CLOSE CUR1;
       
END
;
--작업 종료
 
cs

           - 11 ln: 동적 쿼리를 저장할 변수.

           - 14~15 ln: REF CURSOR로 MY_CURSOR를 선언하여 사용.

           - 25~31 ln: 텍스트로 작성된 쿼리, 텍스트로 작성되었기 때문에 자유롭게 변형이 가능함.

           - 33 ln: FOR문을 사용하여 CURSOR를 OPEN함.

           - 나머지는 5-1과 동일.

       

   5-3. FOR ~ IN 예제.

           - EMP 테이블에서 사원번호, 사원명, 직업, 급여를 조회하시오.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
--실행 결과를 출력하도록 설정
SET SERVEROUTPUT ON
 
--스크립트 경과 시간을 출력하도록 설정
SET TIMING ON
 
 
DECLARE
--변수를 정의하는 영역
 
    V_RESULT_MSG    VARCHAR2(500);
    
    V_EMPNO  SCOTT.EMP.EMPNO%TYPE;
    V_ENAME  SCOTT.EMP.ENAME%TYPE;
    V_JOB  SCOTT.EMP.JOB%TYPE;
    V_SAL  SCOTT.EMP.SAL%TYPE;
 
BEGIN
--작업 영역
 
    FOR CUR1 IN (
        SELECT T1.EMPNO
             , T1.ENAME
             , T1.JOB
             , T1.SAL
          FROM SCOTT.EMP T1
    )
    LOOP
    
        V_RESULT_MSG := CUR1.EMPNO||' / '||CUR1.ENAME||' / '||CUR1.JOB||' / '||CUR1.SAL;
        
        DBMS_OUTPUT.PUT_LINE( V_RESULT_MSG );
    
    END LOOP
    ;
 
END
;
--작업 종료
cs

           - 13~16 ln: EMP 테이블의 컬럼을 참조하여 데이터의 타입을 정의.

           - 21~26 ln: 데이터를 조회하여 CUR1을 생성하고 OPEN함.

           - 28~34 ln: 내부적으로 FETCH가 실행되며 FETCH 할 데이터가 없을 경우 CLOSE됨.

 

 

6. 마치며

   - CURSOR는 PL/SQL을 사용하면 땔래야 땔 수 없는 기능이므로 반드시 숙지해두길 바란다.

   - 묵시적 CURSOR는 사용자가 관리하는 영역이 아니기 때문에 크게 신경쓸 필요 없다. 주로 명시적 CURSOR로 FETCH된 데이터를 INSERT, UPDATE, DELETE 등의 데이터를 수정한 결과를 가져올 때 사용된다.

   - 명시적 CURSOR는 모두 다 알아두면 좋지만 특별한 경우가 아니라면 'FOR ~ IN' CURSOR만 알고 있어도 충분하다. 실제로도 가장 많이 사용되며, 파티션을 조회하거나 동적으로 컬럼을 변경하여 조회해야하는 경우에 5-2의 'REF CURSOR'를 사용한다.

댓글