🎯 학습 목표
- 명시적 커서의 네 단계(DECLARE/OPEN/FETCH/CLOSE)를 작성한다.
- %NOTFOUND로 FETCH 루프를 안전하게 종료한다.
- 커서 FOR 루프로 더 간결하게 여러 행을 처리한다.
- 파라미터가 있는 커서를 만들고 호출한다.
📖 왜 명시적 커서가 필요한가
SELECT INTO는 결과가 정확히 한 행일 때만 쓸 수 있습니다. 여러 행을 한 건씩 꺼내 처리하려면 명시적 커서(Explicit Cursor)를 사용합니다. 커서는 결과 집합을 가리키는 포인터로, FETCH를 호출할 때마다 한 행씩 가져옵니다.
💻 OPEN / FETCH / CLOSE 패턴
DECLARE
-- 1) DECLARE: 커서 선언 (SQL 정의)
CURSOR c_emp IS
SELECT empno, ename, sal
FROM emp
WHERE deptno = 20
ORDER BY sal DESC;
v_empno emp.empno%TYPE;
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
-- 2) OPEN: 커서 열기 (쿼리 실행, 결과 집합 준비)
OPEN c_emp;
LOOP
-- 3) FETCH: 한 행 가져와서 변수에 담기
FETCH c_emp INTO v_empno, v_ename, v_sal;
-- 더 가져올 행이 없으면 EXIT (FETCH 직후 바로 확인)
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_empno || ' ' || v_ename || ' 급여: ' || v_sal);
END LOOP;
-- 4) CLOSE: 커서 닫기 (자원 반납)
CLOSE c_emp;
END;
/
📖 커서 속성
커서명%FOUND : 마지막 FETCH가 행을 가져왔으면 TRUE
커서명%NOTFOUND : 마지막 FETCH가 행을 못 가져왔으면 TRUE (루프 종료 조건)
커서명%ROWCOUNT : 지금까지 FETCH한 행 수
커서명%ISOPEN : 커서가 열려 있으면 TRUE
💻 커서 FOR 루프 — 가장 간결한 방법
커서 FOR 루프는 OPEN·FETCH·CLOSE를 자동으로 처리합니다. 대부분의 경우 이 방법이 가장 간결하고 안전합니다.
-- 커서를 미리 선언하지 않아도 인라인으로 쓸 수 있음
BEGIN
FOR r IN (SELECT empno, ename, sal
FROM emp
WHERE deptno = 20
ORDER BY sal DESC) LOOP
DBMS_OUTPUT.PUT_LINE(r.empno || ' ' || r.ename || ' 급여: ' || r.sal);
END LOOP;
-- 루프가 끝나면 커서가 자동으로 닫힘
END;
/
레코드 변수 r은 자동 선언되며, r.컬럼명으로 각 필드에 접근합니다.
💻 파라미터 커서
DECLARE
-- 부서 번호를 파라미터로 받는 커서
CURSOR c_dept_emp(p_deptno emp.deptno%TYPE) IS
SELECT ename, sal FROM emp WHERE deptno = p_deptno;
BEGIN
DBMS_OUTPUT.PUT_LINE('=== 부서 10 ===');
FOR r IN c_dept_emp(10) LOOP
DBMS_OUTPUT.PUT_LINE(r.ename || ': ' || r.sal);
END LOOP;
DBMS_OUTPUT.PUT_LINE('=== 부서 30 ===');
FOR r IN c_dept_emp(30) LOOP
DBMS_OUTPUT.PUT_LINE(r.ename || ': ' || r.sal);
END LOOP;
END;
/
파라미터 커서는 같은 SQL 구조로 다른 값에 대해 반복 사용할 때 편리합니다.
⚠️ 주의사항
- OPEN한 커서는 반드시 CLOSE해야 자원(세션 메모리)이 반납됩니다. 커서 FOR 루프는 자동으로 닫아 줍니다.
- FETCH 직후
EXIT WHEN %NOTFOUND를 두지 않으면 마지막으로 가져온 행을 두 번 처리할 수 있습니다. FETCH 직후에 반드시 확인하세요. - 이미 열려 있는 커서를 다시 OPEN하면 오류가 납니다.
%ISOPEN으로 확인 후 열거나 먼저 CLOSE하세요.
💡 팁
- 단순 반복 처리라면 커서 FOR 루프(인라인 포함)를 기본으로 쓰세요. OPEN/FETCH/CLOSE 패턴은 중간에 루프를 특수하게 제어해야 할 때만 씁니다.
- 커서를 쓰는 것보다 하나의 UPDATE/INSERT … SELECT로 집합 처리하는 게 빠를 때가 많습니다. “정말 행마다 다른 처리가 필요한가?”를 먼저 검토하세요.