Dynamic SQL

프로그램이 실행될 때 SQL 문장의 내용을 동적으로 지정하는 Dynamic SQL를 설명합니다.

개요

tbESQL/C 프로그램은 컴파일을 할 때 미리 정해진 완전한 SQL 문장을 실행할 수도 있지만 일부 애플리케이션에서는 컴파일을 할 때가 아닌 프로그램을 실행할 때 완전한 SQL 문장이 정해지는 경우도 있습니다.

Dynamic SQL 문장은 이러한 경우를 위해 프로그램을 실행할 때 사용자의 입력 등에 따라 유연하게 SQL문장을 완성하여 실행하는 인터페이스입니다.

Dynamic SQL은 SELECT 문장인지 아닌지, 또 문장 내에 입력 변수의 포함 여부에 따라 다른 순서로 실행됩니다.이때 입력 변수와 출력 변수의 개수와 데이터 타입은 컴파일을 할 때 알고 있어야 합니다. Dynamic SQL 문장은 프로그램을 실행할 때 완성되므로 WHERE 절 등의 조건식을 동적으로 변경할 수도 있습니다.

Dynamic SQL을 사용하는 경우는 다음의 두 가지입니다.

  • SQL 문장의 일부가 완성되지 않은 경우 예를 들어 UPDATE 문장의 WHERE 절의 조건이 프로그램이 실행될 때 정해지는 경우를 들 수 있습니다.

  • SQL 문장을 실행할 대상이 정해지지 않은 경우 예를 들어 GRANT 문장을 이용하여 특권을 부여할 사용자가 프로그램이 실행될 때 정해지는 경우를 들 수 있습니다.

특징

Dynamic SQL 문장은 다음과 같은 특징이 있습니다.

  • 문자열 데이터입니다. tbESQL/C 프로그램에서 EXEC SQL을 통해 실행되는 일반 SQL 문장은 프로그램 내에 직접 포함되지만, Dynamic SQL 문장은 문자열 타입 데이터 형태로 포함됩니다. 즉, 문자열 변수에 저장되어 있거나 문자열 값으로 표현됩니다.

  • 시작과 끝을 나타내는 부호가 없습니다. Dynamic SQL 문장 내에는 시작을 나타내는 EXEC SQL이나 끝을 나타내는 세미콜론(;)이 포함되지 않습니다.

다음은 Dynamic SQL 문장의 몇 가지 예입니다.

'DELETE FROM EMP WHERE SALARY < 20000'
'UPDATE EMP SET SALARY = SALARY * 1.05 WHERE DEPTNO = :deptno'
'SELECT ENAME, ADDR, SALARY FROM EMP WHERE DEPTNO = :deptno'

실행 방법

Dynamic SQL 문장을 실행하는 방법에는 다음의 4가지가 있습니다.

  • 방법 1 : 입력 변수를 포함하지 않는 SELECT 이외의 문장을 실행합니다.

  • 방법 2 : 입력 변수를 포함하는 SELECT 이외의 문장을 실행합니다.

  • 방법 3 : SELECT 문장의 실행 방법이며, 입력 변수를 포함할 수도 있고 포함하지 않을 수도 있습니다.

  • 방법 4 : SELECT 문장으로 조회할 컬럼의 개수 및 데이터 타입을 프리컴파일 시점에서 알 수 없을 때 사용합니다.

방법 1

방법 1은 입력 변수를 포함하지 않는 SELECT 이외의 문장을 동적으로 실행하는 방법입니다. 이 방법은 하 나의 SQL 문장을 실행할 때 동적으로 생성하여 EXCUTE IMMEDIATE 문장을 통하여 실행합니다. 이 방법은 동일한 SQL 문장을 실행하더라도 실행할 때마다 다시 파싱(Parsing)과 최적화를 수행합니다.

SQL 문장을 실행하기 위한 EXECUTE IMMEDIATE의 문법은 다음과 같습니다.

항목
설명

sql_var

  • SQL 문장을 포함하는 문자열 타입 변수

  • VARCHAR 타입이거나 CHAR 타입 모두 가능

sql_stmt

문자열 형태의 SQL 문장

사용 예제

다음은 방법 1을 통해 실행 가능한 SQL 문장의 예입니다.

다음은 방법 1을 통해 SQL 문장을 실행하는 예입니다.

VARCHAR 타입의 변수인 경우에는 len 변수의 값도 정확하게 설정하여야 합니다. 위의 예에서는 소스 코드 'sql_var2.len = strlen(sql_var2.arr);'을 통해 len 변수의 값을 설정하고 있습니다.

방법 1을 통해 SQL 문장을 실행할 때는 실행할 때마다 다시 파싱과 최적화 과정을 거치기 때문에 DDL 문장처럼 한 번만 실행하기 위한 문장에 적합합니다. 만약 SQL 문장 내의 일부 값만을 변화시켜 실행하려면, 다음 절에서 설명할 방법 2가 더 적합합니다.

방법 2

방법 2는 입력 변수를 포함하는 SELECT 이외의 문장을 동적으로 실행하는 방법입니다.

이 방법을 사용하면, 입력 변수만 다르고 나머지 내용은 동일한 SQL 문장을 실행하는데 유용합니다. 그 이유는 문장마다 매번 파싱을 수행하지 않고, 파싱을 한 번만 수행한 뒤 바로 최적화를 수행하기 때문입니다.

이 방법을 통하여 실행하는 SQL 문장 내의 입력 변수의 개수와 데이터 타입에는 제한이 없으나, 컴파일을 할 때 미리 알고 있어야 합니다. 입력 변수가 없는 SELECT 문장 이외의 다른 문장을 방법 2를 통하여 실행 할 수도 있습니다. 하지만 PREPARE와 EXECUTE의 두 단계를 거쳐야 하므로, 그러한 문장은 방법 1을 통해 실행하는 것이 유리합니다.

이 방법은 Dynamic SQL 문장을 다음의 두 단계에 걸쳐 실행합니다.

  1. PREPARE 단계 SQL 문장을 준비하기 위한 PREPARE의 문법은 다음과 같습니다.

항목
설명

stmt_name

준비된 SQL 문장을 대표하는 이름이며 EXECUTE 문장에서 사용됨

sql_var

  • SQL 문장을 포함하는 문자열 타입 변수

  • VARCHAR 타입이거나 CHAR 타입 모두 가능

sql_stmt

문자열 형태의 SQL 문장

PREPARE 문을 통하여 준비된 SQL 문장은 COMMIT 또는 ROLLBACK 문을 실행한 후에도 사용할 수 있습니다. 이러한 문장들은 현재 세션이 끝날 때까지 유효합니다.

  1. EXECUTE 단계

준비된 SQL 문장을 실행하기 위한 EXECUTE의 문법은 다음과 같습니다.

항목
설명

stmt_name

실행할 SQL 문장이며, PREPARE를 사용해 준비된 문장의 이름

USING

  • 실행하려는 SQL 문장에 입력 변수가 있는 경우 입력 변수의 값을 할당하기 위해 사용됨

  • USING 절에 나열되는 변수의 개수와 데이터 타입은 SQL 문장 내의 입력 변수의 개 수와 데이터 타입과 동일해야 함

  • USING 절에 나열된 변수의 데이터 타입이 준비 된 SQL 문장의 입력 변수의 데이터 타입과 다르더라도 데이터 값의 변환이 가능하다면 해당 변수의 사용이 가능

  • 예를 들어 USING에 나열된 변수의 데이터 타입이 NUMBER이며, 이에 대응되는 입력 변수의 데이터 타입이 VARCHAR라면, 데이터 값이 변환되어 입력 변수에 할당됨

  • USING 절에서 변수를 나열할 때 지시자 변수를 함께 포함할 수도 있음

  • 지시자 변수는 NULL 값 등을 표현할 경우에 사용될 수 있음

  • USING 절의 변수는 배열 변수를 사용할 수도 있음

  • 이때 USING 절 내의 모든 변수가 배열 변수이어야 하며 같은 크기를 가져야 함

host_var

입력 변수로 사용될 호스트 변수

ind_var

지시자 변수

문장 이름 stmt_name은 PREPARE를 이용해 여러 SQL 문장을 준비한 경우에 EXECUTE 문장에서 하나의 문장을 지정하기 위하여 사용 합니다. 즉, 문장 이름은 프로그램 모듈 내에서 유일하게 선언해야 합니다. 문장 이름은 프로그램 변수가 아니며 DECLARE 영역 내에서 선언하지 않고, PREPARE 문장 내에서만 선언됩니다.

circle-info

참고

  1. tbESQL/C 프로그램에서의 데이터 타입 변환은 “2.3. tbESQL/C 데이터 타입”을 참고합니다.

  2. 지시자 변수에 대해서는 “2.3.8. 지시자”를 참고합니다.

사용 예제

다음은 방법 2를 통해 실행 가능한 SQL 문장의 예입니다.

다음은 방법 2를 통해 SQL 문장을 실행하는 예입니다.

위의 예에서는 두 개의 입력 변수를 갖는 UPDATE 문장 하나를 두 번 실행하였습니다.

방법 3

방법 3은 SELECT 문장을 동적으로 실행하는 방법입니다. 입력 변수를 포함할 수도 있고 포함하지 않을 수도 있습니다. 동적 SELECT 문장에 포함되는 입/출력 변수의 개수와 데이터 타입에는 제한이 없으나, 컴파일을 할 때 미리 알고 있어야 합니다. 하지만 FROM 절에 포함되는 테이블과 뷰, WHERE 절 내의 조건식, GROUP BY 절, ORDER BY 절 등은 실행을 할 때 정해져도 무방합니다.

방법 3은 SELECT 문의 실행 결과를 얻기 위하여 커서를 사용하기 때문에, Dynamic SQL 문장을 실행할 때 다섯 단계를 거칩니다. 전체적인 실행 순서를 설명하면 다음과 같습니다.

  1. PREPARE 단계

PREPARE를 실행합니다. PREPARE는 주어진 SQL 문장을 해석하고 실행할 준비를 합니다. PREPARE의 문법 및 사용 방법은 “방법 2”와 동일합니다.

  1. DECLARE 단계

SQL 문장을 준비한 후에는 DECLARE를 이용하여 커서 또는 스크롤 가능 커서를 선언합니다. DECLARE의 문법은 다음과 같습니다.

항목
설명

cursor_name

  • 선언하는 커서의 이름

  • 커서 이름은 변수가 아니며 DECLARE 내에서만 선언됨

  • DECLARE를 이용해 여러 개의 커서를 선언한 경우에 OPEN, FETCH, CLOSE 등의 문장에서 하나의 커서를 지정하기 위하여 사용

stmt_name

PREPARE를 통해 준비된 SELECT 문장의 이름

  1. OPEN 단계

커서를 선언한 후에 커서에 연관된 SELECT 문장을 실행하기 위하여 OPEN을 실행합니다. OPEN은 먼저 USING 절에 나열된 변수 값을 SELECT 문장의 입력 변수에 할당하고, 커서에 메모리 영역을 할당한 뒤 SELECT 문장을 실행합니다. OPEN 문은 커서를 질의 결과 로우의 맨 처음 로우에 위치시킵니다.

OPEN의 문법은 다음과 같습니다.

항목
설명

cursor_name

OPEN을 통해서 열 커서의 이름

USING

SELECT 문장의 입력 변수로 사용될 변수를 나열할 때 사용

host_var

입력 변수로 사용될 호스트 변수

ind_var

지시자 변수

커서 이름은 SQL 문장의 이름과 연관되지만, 문장 이름이 의미하는 실제 SQL 문장과는 직접적으로 연관되어 있지 않습니다. 따라서, 어떤 커서를 문장 이름에 대해 선언한 후 그 문장 이름이 의미하는 실제 SQL 문장이 변경되었을 경우에도 커서에 OPEN을 수행하면 변경된 SQL 문장이 실행됩니다.

다음은 이러한 커서의 이름과 문장의 이름 사이의 연관에 대한 예입니다.

위의 예의 마지막 라인에서 cursor1에 대해 OPEN을 수행하면 sql_var1이 아닌 sql_var2에 저장된 SQL문장이 실행됩니다.

  1. FETCH 단계

질의 결과 로우를 하나씩 추출하기 위하여 FETCH를 이용합니다. 추출된 로우의 각 컬럼 값은 INTO절 내의 변수에 저장됩니다. 지시자 변수는 NULL 값 등을 표현하기 위하여 선택적으로 사용됩니다. 만약 질의 결과 로우가 하나도 없거나 더 이상 읽을 로우가 없다면 NOT FOUND 에러를 반환합니다. 스크롤 가능 커서로 선언한 경우에는 원하는 특정 위치의 로우를 선택하여 읽을 수 있습니다.

FETCH의 문법은 다음과 같습니다.

커서 이름 바로 앞에 있는 옵션은 액세스하려는 로우의 위치를 의미합니다. 스크롤 가능 커서에 대한 설명과 각 옵션의 의미는 “스크롤 가능 커서”를 참고합니다.

  1. CLOSE 단계

커서를 이용해 원하는 모든 로우를 읽은 후에는 해당 커서에 CLOSE를 실행합니다. CLOSE는 커서에 할 당된 메모리를 반환하고 커서를 닫습니다. 닫힌 커서에 대해서는 FETCH를 실행할 수 없습니다.

CLOSE의 문법은 다음과 같습니다.

항목
설명

cursor_name

CLOSE를 통해서 닫을 커서의 이름

사용 예제

다음은 방법 3을 통해 실행 가능한 SQL 문장의 예입니다.

다음은 방법 3을 통해 SQL 문장을 실행하는 예입니다.

위의 예에서는 'emp_cursor'라는 이름으로 커서를 선언하였으며, NOT FOUND 에러가 반환될 때까지 while 루프 내의 FETCH 문을 계속 실행합니다.

방법 4

방법 4는 SELECT 문장을 통해 조회할 컬럼의 개수 및 타입을 프리컴파일 시점에서 알 수 없을 때 사용하는 방법입니다. 이 방법에 사용되는 핵심적인 자료 구조는 SQLDA라는 구조체입니다. SQLDA 타입의 서술자 (Descriptor) 변수를 이용해서 바인드 변수와 조회할 컬럼을 기술하여 Dynamic SQL을 수행합니다.

다음은 SQLDA 구조체의 선언 방법 및 각 멤버 변수를 주석을 통해 설명합니다.

Dynamic Method 4를 사용할 때의 binding 가능한 host variable type이 정해져 있습니다.

다음은 SQLDA 구조체의 타입 변수인 T에 맵핑시킬 수 있는 External 데이터 타입과 타입 코드를 설명합니다.

External Type
Type Code
C type

VARCHAR2

1

char[n]

NUMBER

2

char[n] ( n < 22 )

INTEGER

3

int

FLOAT

4

float

STRING

5

char[n+1]

VARNUM

6

char[n] ( n < 22 )

DECIMAL

7

float

LONG

8

char[n]

BINARY_FLOAT

21

float

BINARY_DOUBLE

22

double

VARCHAR

9

char[n+2]

ROWID

11

char[n]

DATE

12

char[n]

VARRAW

15

char[n]

RAW

23

unsigned char[n]

LONG RAW

24

unsigned char[n]

UNSIGNED

68

unsigned int

DISPLAY

91

char[n]

LONG VARCHAR

94

char[n+4]

LONG VARRAW

95

unsigned char[n+4]

CHAR

96

char[n]

CHARF

96

char[n]

CHARZ

97

char[n+1]

CLOB

112

Cloblocator

BLOB

113

Bloblocator

방법 4의 전체적인 실행 순서를 설명하면 다음과 같습니다.

  1. 먼저 SQLDA 변수를 선언합니다.

다음은 SQLDA 변수를 선언하는 예입니다.

  1. 각 변수에 대하여 SQLSQLDAAlloc 함수를 이용해서 메모리 공간을 할당합니다.

다음은 SQLSQLDAAlloc 함수를 통해 메모리 공간을 할당하는 예입니다.

  1. 각 서술자에 사용될 컬럼의 개수와 변수의 개수를 지정합니다.

다음은 서술자에 사용될 컬럼의 개수와 변수의 개수를 지정하는 예입니다.

  1. 호스트 변수에 미리 지정된 SQL 문장을 PREPARE를 사용해 준비합니다.

다음은 SQL 문장을 준비하는 예입니다.

  1. DECLARE를 사용해 커서를 선언합니다.

다음은 커서를 선언하는 예입니다.

  1. DESCRIBE로 SQL 문장의 바인드 변수를 바인딩합니다.

다음은 바인드 변수를 바인딩을 하는 예입니다.

  1. DESCRIBE 문장을 통해 실제 계산된 바인드 변수의 개수를 다시 지정합니다.

bind_dp->F의 값이 2임을 알고 난 후 다음과 같이 설정해 줄 수 있습니다.

  1. 바인드 변수를 위한 메모리를 할당합니다.

여기서 bind_dp->F의 값이 2가 아닌 다른 수였다면 그에 맞춘 값 할당이 필요합니다. for 루프 등을 사용 해 해당 값을 알맞게 할당하는 프로그램을 작성할 수도 있습니다. 다음은 바인드 변수를 위한 메모리 공간을 할당하는 예입니다.

  1. OPEN을 사용해 준비된 바인드 서술자로 커서를 엽니다.

다음은 바인드 서술자를 사용해 커서를 여는 예입니다.

  1. 조회할 컬럼에 대한 서술자를 작성합니다.

다음은 서술자를 작성하는 예입니다.

  1. 실제 조회할 컬럼의 개수를 알게 되었으므로 이를 재설정합니다.

다음은 컬럼의 개수를 재지정하는 예입니다.

  1. 조회할 각 컬럼의 길이와 데이터 타입을 재설정합니다.

다음은 select_dp->F 값이 1, 즉 select list의 개수가 1이라고 가정하고 재설정하는 예입니다.

select_dp->F의 개수에 따라 select_dp의 각 변수의 설정이 달라져야 합니다.

13. 서술자를 이용해서 FETCH를 실행합니다.

다음은 FETCH를 실행하는 예입니다.

수행결과는 서술자의 V변수에 값이 저장됩니다.

  1. FETCH의 실행을 완료한 후에는 할당된 메모리를 모두 해제합니다.

다음은 할당된 메모리를 해제하는 예입니다.

  1. 마지막으로 CLOSE를 사용해 커서를 닫습니다.

다음은 커서를 닫는 예입니다.

사용 예제

다음은 방법 4를 통해 배열변수를 이용한 SQL 문장을 실행하는 예입니다.

Last updated