-
커서 ( Cursor )SQL/PL. SQL 2020. 2. 24. 20:27
커서 (Cursor)
- SQL문을 실행하기 위한 메모리 공간
-> 특정 SQL 문장을 처리한 결과를 담고 있는 영역을 가리키는 일종의 포인터
- 커서를 사용하면 처리된 SQL 문장
- 기존에 사용한 SQL 문은 묵시적 커서를 사용
종류
1. 묵시적(암시적) 커서
1) 오라클 내부에서 자동으로 생성되어 사용하는 커서
2) PL/SQL 블록에서 실행되는 문장
3) ( INSERT, UPDATE, MERGE, DELETE, SELECT INTO )가 실행될 때마다 자동으로 만들어져 사용 됌
4) 개발자 입장에서는 이러한 커서의 동작에 관여할 수 없음
5) 커서 속성을 이용하면 해당 커서에 대한 여러가지 정보를 알아낼 수 있음
* 묵시적 커서 속성
1) SQL%FOUND : 결과 집합의 패치 로우 수가 1개 이상이면 TRUE, 아니면 FALSE
2) SQL%NOTFOUND : 결과 집합의 패치 ROW수가 0이면 TRUE, 아니면 FALSE
3) SQL%ROWCOUNT : 영향 받은 결과 집합의 ROW 수 반환, 없으면 0 반환
4) SQL%ISOPEN : 묵시적 커서는 항상 FALSE를 반환
(이 속성으로 참조할 때는 이미 해당 묵시적 커서는 닫힌 상태 이후이기 때문)
예제
DECLARE V_dept_id emp.dept_id%type := 80; BEGIN UPDATE emp SET ename = ename; WHERE dept_id = V_dept_id; DBMS_OUTPUT.PUT.LINE(SQL%ROWCOUNT); -> 몇 건의 데이터가 갱신 되었는지 출력 COMMIT; END; /
2. 명시적 커서
1) 사용자가 직접 정의해서 사용하는 커서
2) 사용할 커서에 이름을 부여하고 이 커서에 대한 쿼리를 선언해야 함
3) 명시적 커서는 결과 데이터 집합을 ROW별로 참조해서 어떠한 작업을 하기 위한 용도이므로
커서를 정의해 사용하는 문장은 SELECT이다.
4) SELECT 결과 여러건을 TABLE 타입의 변수에 저장할 수 있지만 메모리는 한정적이기 때문에
많은 양의 데이터를 담기에는 제한이 따름
-> SQL 커서를 통해 개발자가 직접 데이터를 FETCH함으로써 SELECT 결과를 전부 불러오지 않고도 개발이 가능
- 커서 선언 방법
DECLARE -> 선언부에서 선언 CURSOR 커서이름 IS 제어할 쿼리;
- 커서 열기
BEGIN -> 실행부에서 커서 열기 OPEN 커서이름;
- 커서로부터 데이터 FETCH
FETCH 커서이름 INTO 변수; -> 위 실행부의 커서 열기에 이어서
- 커서 닫기
CLOSE 커서이름; -> 실행부에서 커서 닫기 까지
실습
- 부서 테이블을 활용하여 모든 행에 대해 부서번호와 부서 이름을 cursor를 통해 FETCH하고 결과 확인
SET SERVEROUTPUT ON; DECLARE v_deptno dept.deptno%TYPE; v_dname dept.dname%TYPE; CURSOR dept_cursor IS SELECT deptno, dname FROM dept; BEGIN OPEN dept_cursor; LOOP FETCH dept_cursor INTO v_deptno, v_dname; EXIT WHEN dept_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_deptno || ' : ' || v_dname); END LOOP; END; /
※ 커서를 열고 닫는 과정이 다소 길기 때문에 Cursor는 일반적으로 loop와 함께 사용함
=> 명시적 커서를 FOR LOOP에서 사용할 수 있게 끔 문법으로 제공
* JAVA의 향상된 FOR문과 유사한 형태
JAVA for(String UserName : userNameList){ UserName 값을 사용..; }
PL/SQL FOR record_name(한 행의 정보를 담을 변수이름 / 변수를 직접 선언안함) IN 커서이름 LOOP record_name.컬럼명 END LOOP;
실습
DECLARE v_deptno dept.deptno%TYPE; v_dname dept.dname%TYPE; CURSOR dept_cursor IS SELECT deptno, dname FROM dept; BEGIN FOR rec IN dept_cursor LOOP DBMS_OUTPUT.PUT_LINE(rec.deptno || ' : ' || rec.dname); END LOOP; END; /
인자가 있는 커서
기존의 커서 선언 방법
CURSOR 커서이름 IS 서브쿼리..;
인자가 있는 커서 선언 방법
CURSOR 커서이름(인자1 인자1 타입, 인자2 인자2 타입, ...) IS 서브쿼리; (커서 선언시에 작성한 인자를 서브쿼리에서 사용할 수 있다.)
실습
DECLARE v_deptno dept.deptno%TYPE; v_dname dept.dname%TYPE; CURSOR dept_cursor(p_deptno dept.deptno%TYPE) IS SELECT deptno, dname FROM dept WHERE deptno <= p_deptno; BEGIN FOR rec IN dept_cursor(:deptno) LOOP DBMS_OUTPUT.PUT_LINE(rec.deptno || ' : ' || rec.dname); END LOOP; END; /
더 간단하게 FOR LOOP 에서 커서를 인라인 형태로 작성
FOR 레코드 이름 IN 커서이름 ==> FOR 레코드 이름 IN (서브쿼리);
실습
DECLARE BEGIN FOR rec IN (SELECT deptno, dname FROM dept) LOOP DBMS_OUTPUT.PUT_LINE(rec.deptno || ' : ' || rec.dname); END LOOP; END; /
심화실습
CREATE TABLE DT ( DT DATE); insert into dt select trunc(sysdate + 10) from dual union all select trunc(sysdate + 5) from dual union all select trunc(sysdate) from dual union all select trunc(sysdate - 5) from dual union all select trunc(sysdate - 10) from dual union all select trunc(sysdate - 15) from dual union all select trunc(sysdate - 20) from dual union all select trunc(sysdate - 25) from dual; commit; SELECT * FROM dt;
위의 테이블 데이터들의 평균차이값을 구해라
DECLARE TYPE dt_tab IS TABLE OF dt%ROWTYPE INDEX BY BINARY_INTEGER; v_dt_tab dt_tab; v_sum number(5) := 0; v_avg number(5) := 0; BEGIN SELECT * BULK COLLECT INTO v_dt_tab FROM dt; FOR i IN 1..(v_dt_tab.count-1) LOOP v_sum := v_sum + TO_NUMBER(TO_CHAR(v_dt_tab(i).dt - v_dt_tab(i+1).dt)); DBMS_OUTPUT.PUT_LINE(v_sum); END LOOP; v_avg := v_sum/(v_dt_tab.count-1); DBMS_OUTPUT.PUT_LINE(v_avg || ' , '|| v_sum); END; /
위의 쿼리를 분석함수를 써서 SQL로 작성
SELECT avg(gap) from ( SELECT DT, lead_dt, dt-lead_dt GAP FROM ( SELECT dt, LEAD(DT) OVER(ORDER BY DT DESC) lead_dt FROM dt)); SELECT (MAX(dt) - MIN(dt)) / (COUNT(dt)-1) avg FROM dt;
'SQL > PL. SQL' 카테고리의 다른 글
PL/SQL 예외처리(EXCEPTION) (0) 2020.02.25 PL/SQL 날짜에 따른 애음주기 쿼리 조회 실습 (0) 2020.02.25 PL/SQL IF문, CASE문 FOR LOOP문 (0) 2020.02.24 PL/SQL 실습 (0) 2020.02.23 PL/SQL (0) 2020.02.23