프로그래밍

Oracle PL/SQL - 동적 SQL

RainIron 2021. 9. 24. 17:49
반응형
  동적 SQL
사용 방법 실행 중 변경 가능한 문자열 변수 또는 문자열 상수로 제공된 SQL문을 실행
사용 예 EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM emp' INTO v_cnt;
사용 가능한 SQL 모든 SQL문(DML, TCL, DDL, DCL)
장점 - 사전에 정의되지 않은 SQL을 실행 시에 확정하여 실행할 수 있다.
- DML과 TCL외에도 DDL, DCL을 사용할 수 있다.
단점 프로그램을 작성하기가 복잡하다.
용도 - SQL이 몇 개 문장으로 고정될 수 없는 경우
- SQL이 실행될지 예측할 수 없는 경우

* 실행 방법

1. EXECUTE IMMEDIATE문을 사용하여 SQL을 실행하는 방법

2. 커서 변수를 사용하여 OPEN, FETCH, CLOSE문으로 SELECT문을 실행하는 방법

3. 오라클 내장 패키지인 DBMS_SQL을 사용하는 방법

 

1. EXECUTE IMMEDIATE문

- 모든 유형의 SQL문 동적 실행 가능

EXECUTE IMMEDIATE SQL문자열 [INTO 변수목록] [USING 바인드변수목록];

(예시)

DECLARE
    v_insert_stmt CONSTANT VARCHAR2(100) := 'INSERT INTO t VALUES(1, ''서울'')';
BEGIN 
    BEGIN
        EXECUTE IMMEDIATE 'DROP TABLE t';
    -- 예외 발생 처리 구문
    EXCEPTION WHEN OTHERS THEN
        NULL;
    END;
    
    EXECUTE IMMEDIATE 'CREATE TABLE t(a NUMBER, b VARCHAR2(10))';
    
    EXECUTE IMMEDIATE v_insert_stmt;
    
    EXECUTE IMMEDIATE 'COMMIT';
END;

* [INTO 변수목록] : "INTO 레코드 변수"와 같이 사용하여 쿼리 결과를 변수에 저장할 수 있다.

 

* 바인드 변수: ':플레이스홀더명'으로 지정

(예시)

DECLARE
    v_query CONSTANT VARCHAR2(200) := 'SELECT COUNT(*)
                                        FROM emp
                                        WHERE deptno = :deptno
                                        AND job = :job';
    v_deptno emp.deptno%TYPE;
    v_cnt PLS_INTEGER;
BEGIN
    v_deptno := 20;
    
    EXECUTE IMMEDIATE v_query
        INTO v_cnt
        USING IN v_deptno, 'CLERK';
    DBMS_OUTPUT.PUT_LINE('COUNT = '||v_cnt);
END;

- USING절에 바인드 변수를 사용한다.

- IN: 입력 모드, 값이 PL/SQL에서 DB 서버로 전달된다. 입력 모드 변수의 값은 동적 SQL의 실행 전후에 변경이 없다. 모드를 생략할 경우 기본으로 사용된다.

- OUT: 출력 모드, 값이 DB 서버에서 PL/SQL로 전달된다.

- IN OUT: 입출력 모드, 값이 양방향으로 전달된다. 동적 SQL을 실행하기 전에 가지고 있던 값이 DB 서버로 전달되어 참조되며, DB서버에서 변경된 값이 다시 PL/SQL 변수로 되돌려진다.

 

- 다수를 사용할 경우, 변수명과 동일하지 않게 순서대로 대입한다.

 

2. DBMS_SQL

  의미
DBMS_SQL.DESC_TAB DESC_REC 레코드 타입을 요소로 하는 Associative Array
" TYPE desc_tab IS TABLE OF desc_rec INDEX BY BINARY_INTEGER "
DBMS_SQL.DESC_REC 동적 쿼리의 단일 컬럼 정보를 담고 있는 레코드 타입
DBMS_SQL.OPEN_CURSOR 커서를 열고 번호를 반환받는다.
DBMS_SQL.PARSE(커서(번호), 쿼리, 언어)  SQL 파싱, 언어 파트에 DBMS_SQL.NATIVE를 입력한다.
DBMS_SQL.NATIVE  
DBMS_SQL.BIND_VARIABLE 주어진 커서의 SQL 문장에서 콜론(:)으로 시작하는 변수에 값을 설정해주는 프로시저. 변수는 이름으로 찾기 때문에, SQL 문장 내의 변수 이름과 파라미터로 주어진 이름을 동일하게 호출해야 한다.
DBMS_SQL.DESCRIBE_COLUMNS(커서(번호), 컬럼 갯수, 컬럼 정보 저장 테이블 객체) 컬럼 정보 저장 테이블 객체 = DBMS_SQL.DESC_TAB
DBMS_SQL.DEFINE_COLUMN(커서(번호), 컬럼 순서, 컬럼 유형, 컬럼 크기) 주어진 커서에서 FETCH될 컬럼의 타입을 정의하는 프로시저.
SELECT문에서만 사용 가능
DBMS_SQL.EXECUTE 커서를 실행
DBMS_SQL.FETCH_ROWS FETCH
DBMS_SQL.COLUMN_VALUE 주어진 커서에서 FETCH한 컬럼의 값을 원하는 변수로 가져오는 프로시저.
FETCH한 후 데이터를 가져오는 데 사용.
DBMS_SQL.CLOSE 커서를 닫는다.

(예시)

DECLARE
    v_cursor_id NUMBER;
    v_sql_stmt VARCHAR2(4000) := Q'<SELECT *
                                    FROM emp
                                    WHERE deptno = :deptno
                                    AND hiredate >= TO_DATE(:hiredate, 'YYYY-MM-DD')>'; ' -- 주석처리가 되어 따옴표를 추가했고, 실제로 sql을 실행할 때 제거해야한다.
    
    TYPE vc_array IS TABLE OF VARCHAR2(100);
    v_bind_var vc_array;
    v_bind_val vc_array;
    v_ret   NUMBER;
    
    v_desc_tab DBMS_SQL.DESC_TAB;
    v_col_cnt   PLS_INTEGER;
    v_str_var   VARCHAR2(100);
    v_num_var   NUMBER;
    v_date_var  DATE;
    v_row_cnt   PLS_INTEGER;
BEGIN
    v_bind_var := vc_array('deptno', 'hiredate');
    v_bind_val := vc_array('10', '1981-07-01');
    
    -- OPEN CURSOR
    v_cursor_id := DBMS_SQL.OPEN_CURSOR;
    
    -- PARSING
    DBMS_SQL.PARSE(v_cursor_id, v_sql_stmt, DBMS_SQL.NATIVE);
    
    -- BINDING
    FOR i IN 1 .. v_bind_var.COUNT
    LOOP
        DBMS_SQL.BIND_VARIABLE(v_cursor_id, v_bind_var(i), v_bind_val(i));
    END LOOP;
    
    -- DESCRIBE
    DBMS_SQL.DESCRIBE_COLUMNS(v_cursor_id, v_col_cnt, v_desc_tab);
    
    -- DEFINE COLUMN
    FOR i IN 1 .. v_col_cnt LOOP
        IF v_desc_tab(i).col_type = DBMS_SQL.NUMBER_TYPE THEN
            DBMS_SQL.DEFINE_COLUMN(v_cursor_id, i, v_num_var);
        ELSIF v_desc_tab(i).col_type = DBMS_SQL.DATE_TYPE THEN
            DBMS_SQL.DEFINE_COLUMN(v_cursor_id, i, v_date_var);
        ELSE
            DBMS_SQL.DEFINE_COLUMN(v_cursor_id, i, v_str_var, 100);
        END IF;
    END LOOP;
    
    v_ret := DBMS_SQL.EXECUTE(v_cursor_id);
    
    v_row_cnt := 0;
    
    WHILE DBMS_SQL.FETCH_ROWS(v_cursor_id) > 0
    LOOP
        v_row_cnt := v_row_cnt + 1;
        DBMS_OUTPUT.PUT_LINE(v_row_cnt|| '번째 로우');
        FOR i IN 1 .. v_col_cnt LOOP
            IF (v_desc_tab(i).col_type = DBMS_SQL.NUMBER_TYPE) THEN
                DBMS_SQL.COLUMN_VALUE(v_cursor_id, i, v_num_var);
                DBMS_OUTPUT.PUT_LINE(CHR(9)||rpad(v_desc_tab(i).col_name, 8, ' ')||' : '||v_num_var);
            ELSIF (v_desc_tab(i).col_type = DBMS_SQL.DATE_TYPE) THEN
                DBMS_SQL.COLUMN_VALUE(v_cursor_id, i, v_date_var);
                DBMS_OUTPUT.PUT_LINE(CHR(9)||rpad(v_desc_tab(i).col_name, 8, ' ')||' : '||TO_CHAR(v_date_var, 'YYYY-MM-DD'));
            ELSE
                DBMS_SQL.COLUMN_VALUE(v_cursor_id, i, v_str_var);
                DBMS_OUTPUT.PUT_LINE(CHR(9)||rpad(v_desc_tab(i).col_name, 8, ' ')||' : '||v_str_var);
            END IF;
        END LOOP;
    END LOOP;
    
    DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
END;

 

1. OPEN CURSOR

2. PARSING

3. BINDING

4. DESCRIBE

5. DEFINE COLUMN

6. EXECUTE

7. FETCH

8. CLOSE

 

※ 출처: 개발자를 위한 PL/SQL 프로그래밍

반응형

'프로그래밍' 카테고리의 다른 글

Oracle PL/SQL - Trigger  (0) 2021.09.28
Oracle PL/SQL - Stored Subprogram  (0) 2021.09.28
Oracle PL/SQL - Cursor  (0) 2021.09.24
Oracle PL/SQL - Record  (0) 2021.09.23
Oracle PL/SQL - Collection  (0) 2021.09.23