개발자 미니민의 개발스터디

[Oracle] 오라클 - PL/SQL 커서(Cursor) / 동적쿼리

by mini_min
[Oracle]
 오라클 - PL/SQL 커서(Cursor) / 동적쿼리

✔️ 커서(Cursor)

: 하나의 레코드가 아닌, 여러 레코드로 구성된 작업영역에서 SQL 문을 실행하고 그 과정에서 생긴 정보를 저장하기 위해 CURSOR 를 사용한다.

: 오라클 서버에 의해 실행되는 모든 SQL 문은 각각의 커서를 소유한다. 

: 1)암시적 커서 (선언) 2)명시적 커서 (프로그래머가 선언하는 이름있는 커서)

 

 

✔️ 암시적 커서(Cursor)

- SQL%ROWCOUNT : 해당 SQL 문에 영향을 받는 행의 수
- SQL%FOUND : 해당 SQL 영향을 받는 행의 수가 1개 이상일 경우 TRUE
- SQL%NOTFOUND : 해당 SQL 문에 영향을 받는 행의 수가 없을 경우 TRUE
- SQL%ISOPEN : 항상 FALSE, 암시적 커서가 열려 있는지의 여부 검색

 

-- %ROWCOUNT

DECLARE
vEmpNo emp1.empNo%TYPE;
vCount NUMBER;
BEGIN
vEmpNo := '1001';
DELETE FROM emp1 WHERE empNo = vEmpNo;
vCount := SQL%ROWCOUNT; --실행된 쿼리의 갯수 알 수 있음
COMMIT;
DBMS_OUTPUT.PUT_LINE(vCount || '데이터 삭제!');
END;
/

 

 

-- %NOTFOUND

DECLARE
vEmpNo emp1.empNo%TYPE;
vCount NUMBER;
BEGIN
vEmpNo := '1001';
DELETE FROM emp1 WHERE empNo = vEmpNo;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20001, '데이터가 존재하지 않음!');
END IF;
vCount := SQL%ROWCOUNT; --실행된 쿼리의 갯수 알 수 있음
COMMIT;
DBMS_OUTPUT.PUT_LINE(vCount || '데이터 삭제!');
END;
/

 

 

 

✔️ 명시적 커서(Cursor)

- 사용자가 명시적으로 선언한 커서를 말한다.

- 실행순서 : CURSOR 선언 -> 커서 OPEN -> FETCH -> 커서 CLOSE

DECLARE
vName emp.name%TYPE;
vSal emp.sal%TYPE;
-- 1) 커서 선언
CURSOR cur_emp IS SELECT name, sal FROM emp;
BEGIN
-- 2) 커서 오픈
OPEN cur_emp;
LOOP
-- 3) FETCH
FETCH cur_emp INTO vName, vSal;
EXIT WHEN cur_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(vName || ' ' || vSal);
END LOOP;
-- 4) 커서 close
CLOSE cur_emp;
END;
/
💡 커서 선언 : CURSOR 커서명 IS 
커서 오픈 : OPEN 커서명
FETCH 해줘야함
CLOSE 커서명

 

 

-- 프로시저에서 커서 사용하기

CREATE OR REPLACE PROCEDURE pSelectEmp
(
pName VARCHAR2
)
IS
vName emp.name%TYPE;
vSal emp.sal%TYPE;
-- 1) 커서 선언
CURSOR cur_emp IS SELECT name, sal FROM emp WHERE INSTR(name, pName) > 0;
BEGIN
-- 2) 커서 오픈
OPEN cur_emp;
LOOP
-- 3) FETCH
FETCH cur_emp INTO vName, vSal;
EXIT WHEN cur_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(vName || ' ' || vSal);
END LOOP;
-- 4) 커서 close
CLOSE cur_emp;
END;
/
EXEC pSelectEmp('이');

 

 

-- FOR ~ LOOP : 자동으로 OPEN / 자동 FETCH , 자동 CLOSE!

CREATE OR REPLACE PROCEDURE pSelectEmp
(
pName VARCHAR2
)
IS
vName emp.name%TYPE;
vSal emp.sal%TYPE;
CURSOR cur_emp IS SELECT name, sal FROM emp WHERE INSTR(name, pName) > 0;
BEGIN
FOR rec IN cur_emp LOOP
DBMS_OUTPUT.PUT_LINE(rec.name || ' ' || rec.sal);
END LOOP;
END;
/
EXEC pSelectEmp('김');
💡 FOR~ LOOP 에서는 커서 오픈/ 클로즈가 불필요하다. (자동으로 해줌)

 

 

 

✔️ 커서 변수(cursor variable)

-- SYS_REFCURSOR ⭐⭐ 중요함!!!  자바랑 연결하기 해야할 때 필요하다. 

(FOR 문장은 사용 못한다.)

-- 실행된 결과를 담는 그릇 = SYS_REFCURSOR

: 약한 참조 커서 타입

: 데이터 타입을 포함하고 있지 않기 때문에 모든 결과 셋을 저장할 수 있음

: 프로시저 실행결과(SELECT 문)를 자바 등 프로그램에 전달 할때 사용

 

 

⭐ 커서 변수 사용 / 값 가져오기

OPEN 커서변수 FOR SELECT 문
⭐⭐FETCH 커서변수 INTO 변수, 변수;
⭐⭐FETCH 커서변수 INTO 레코드변수;

-- 확인용 프로시저
CREATE OR REPLACE PROCEDURE pResultEmp
(
pName VARCHAR2
)
IS
vName emp.name%TYPE;
vSal emp.sal%TYPE;
vResult SYS_REFCURSOR;
BEGIN
-- 다른 프로시저 호출
pSelectEmp(pName, vResult);
LOOP
FETCH vResult INTO vName, vSal;
EXIT WHEN vResult%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(vName || ' ' || vSal);
END LOOP;
END;
/
EXEC pResultEmp('김');

 

 

 

✔️ 동적 쿼리

-- EXECUTE IMMEDIATE

: DDL, DML 구문을 실행

: SELECT 구문 실행 시 INTO 절을 사용하여 단일 값을 반환 받을 때 사용

: 프로시저 등에서 동적으로 쿼리를 생성하거나 텍스트 쿼리를 입력 받아 처리하는 경우 사용
: RESOURCE 권한만 있으면 기본적으로 테이블생성, 시퀀스 생성등을 할수 있지만 EXECUTE IMMEDIATE 에서는 불가능하다.
: EXECUTE IMMEDIATE 로 테이블을 생성하거나 시퀀스를 만들기 위해서는 다음의 시스템 권한이 필요 한다.
     CREATE TABLE, CREATE SEQUENCE

 

⭐ 권한부터 줘야한다.
GRANT CREATE TABLE TO sky;
GRANT CREATE SEQUENCE TO sky;
-- SKY 계정에서 내 권한 확인하기
SELECT * FROM user_sys_privs;

 

 

-- 동적 쿼리를 이용하여 테이블 및 시퀀스 작성

CREATE OR REPLACE PROCEDURE pBoardCreate
(
pName VARCHAR2
)
IS
s VARCHAR2(4000);
BEGIN
s := 'CREATE TABLE ' || pName;
s := s || '(num NUMBER PRIMARY KEY, ';
s := s || ' name VARCHAR2(50) NOT NULL,';
s := s || ' subject VARCHAR2(500) NOT NULL,';
s := s || ' content VARCHAR2(4000) NOT NULL,';
s := s || ' hitCount NUMBER DEFAULT 0, ';
s := s || ' reg_date DATE DEFAULT SYSDATE)';
EXECUTE IMMEDIATE s;
DBMS_OUTPUT.PUT_LINE(pName || ' 테이블 생성 !!' );
s := 'CREATE SEQUENCE ' || pName || '_seq';
EXECUTE IMMEDIATE s;
DBMS_OUTPUT.PUT_LINE(pName || '_seq 시퀀스 생성 !!' );
END;
/

 

 

-- 동적 쿼리를 이용하여 테이블 및 시퀀스 작성 (삭제, 수정 포함)

CREATE OR REPLACE PROCEDURE pBoardCreate
(
pName VARCHAR2
)
IS
s VARCHAR2(4000);
BEGIN
s := 'CREATE TABLE ' || pName;
s := s || '(num NUMBER PRIMARY KEY, ';
s := s || ' name VARCHAR2(50) NOT NULL,';
s := s || ' subject VARCHAR2(500) NOT NULL,';
s := s || ' content VARCHAR2(4000) NOT NULL,';
s := s || ' hitCount NUMBER DEFAULT 0, ';
s := s || ' reg_date DATE DEFAULT SYSDATE)';
FOR t IN (SELECT * FROM tab WHERE tname = UPPER(pName)) LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || pName || ' PURGE';
DBMS_OUTPUT.PUT_LINE(pName || ' 테이블 삭제 !!' );
EXIT;
END LOOP;
EXECUTE IMMEDIATE s;
DBMS_OUTPUT.PUT_LINE(pName || ' 테이블 생성 !!' );
FOR t IN (SELECT * FROM seq WHERE sequence_name = UPPER(pName||'_seq')) LOOP
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || pName || '_seq';
DBMS_OUTPUT.PUT_LINE(pName || '_seq 시퀀스 삭제 !!' );
EXIT;
END LOOP;
s := 'CREATE SEQUENCE ' || pName || '_seq';
EXECUTE IMMEDIATE s;
DBMS_OUTPUT.PUT_LINE(pName || '_seq 시퀀스 생성 !!' );
END;
/
💡 같은 이름의 테이블/시퀀스가 있으면 삭제하고 바로 다시 만든다.

 

 

 

 

 

 

블로그의 프로필 사진

블로그의 정보

개발자 미니민의 개발로그

mini_min

활동하기