> For the complete documentation index, see [llms.txt](https://docs.tibero.com/tibero-manuals/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tibero.com/tibero-manuals/7.2.6.manuals/tbesql-c-guide/dynamic-sql.md).

# Dynamic SQL

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

## **개요**

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

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

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

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

* SQL 문장의 일부가 완성되지 않은 경우\
  예를 들어 UPDATE 문장의 WHERE 절의 조건이 프로그램이 실행될 때 정해지는 경우를 들 수 있습니다.<br>
* SQL 문장을 실행할 대상이 정해지지 않은 경우\
  예를 들어 GRANT 문장을 이용하여 특권을 부여할 사용자가 프로그램이 실행될 때 정해지는 경우를 들 수 있습니다.

## **특징**

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

* 문자열 데이터입니다.\
  tbESQL/C 프로그램에서 EXEC SQL을 통해 실행되는 일반 SQL 문장은 프로그램 내에 직접 포함되지만, Dynamic SQL 문장은 문자열 타입 데이터 형태로 포함됩니다. 즉, 문자열 변수에 저장되어 있거나 문자열 값으로 표현됩니다.<br>
* 시작과 끝을 나타내는 부호가 없습니다.\
  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**의 문법은 다음과 같습니다.

```
EXEC SQL EXECUTE IMMEDIATE { :sql_var | 'sql_stmt' }
```

<table><thead><tr><th width="141">항목</th><th>설명</th></tr></thead><tbody><tr><td>sql_var</td><td><ul><li>SQL 문장을 포함하는 문자열 타입 변수</li><li>VARCHAR 타입이거나 CHAR 타입 모두 가능</li></ul></td></tr><tr><td>sql_stmt</td><td>문자열 형태의 SQL 문장</td></tr></tbody></table>

#### **사용 예제**

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

```
'DELETE FROM EMP WHERE SALARY < 20000'
```

```
'GRANT SELECT ANY TABLE TO John'
```

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

```
char sql_var1[128]; 
VARCHAR sql_var2[100];
...
strcpy(sql_var1,
        "UPDATE EMP SET SALARY = SALARY * 1.05 WHERE DEPTNO = 5"); 
EXEC SQL EXECUTE IMMEDIATE :sql_var1;
...
strcpy(sql_var2.arr,
        "UPDATE EMP SET SALARY = SALARY * 1.05 WHERE DEPTNO = 6");
sql_var2.len = strlen(sql_var2.arr); 
EXEC SQL EXECUTE IMMEDIATE :sql_var2;
...
EXEC SQL EXECUTE IMMEDIATE 'GRANT SELECT ANY TABLE TO John';
```

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의 문법은 다음과 같습니다.

```
EXEC SQL PREPARE stmt_name FROM { :sql_var | 'sql_stmt' }
```

<table><thead><tr><th width="161">항목</th><th>설명</th></tr></thead><tbody><tr><td>stmt_name</td><td>준비된 SQL 문장을 대표하는 이름이며 EXECUTE 문장에서 사용됨</td></tr><tr><td>sql_var</td><td><ul><li>SQL 문장을 포함하는 문자열 타입 변수</li><li>VARCHAR 타입이거나 CHAR 타입 모두 가능</li></ul></td></tr><tr><td>sql_stmt</td><td>문자열 형태의 SQL 문장</td></tr></tbody></table>

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

2. **EXECUTE** 단계

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

```
EXEC SQL EXECUTE stmt_name [USING :host_var[:ind_var], ...]
```

<table><thead><tr><th width="179">항목</th><th>설명</th></tr></thead><tbody><tr><td>stmt_name</td><td>실행할 SQL 문장이며, PREPARE를 사용해 준비된 문장의 이름</td></tr><tr><td>USING</td><td><ul><li>실행하려는 SQL 문장에 입력 변수가 있는 경우 입력 변수의 값을 할당하기 위해 사용됨</li><li>USING 절에 나열되는 변수의 개수와 데이터 타입은 SQL 문장 내의 입력 변수의 개 수와 데이터 타입과 동일해야 함</li><li>USING 절에 나열된 변수의 데이터 타입이 준비 된 SQL 문장의 입력 변수의 데이터 타입과 다르더라도 데이터 값의 변환이 가능하다면 해당 변수의 사용이 가능</li><li>예를 들어 USING에 나열된 변수의 데이터 타입이 NUMBER이며, 이에 대응되는 입력 변수의 데이터 타입이 VARCHAR라면, 데이터 값이 변환되어 입력 변수에 할당됨</li><li>USING 절에서 변수를 나열할 때 지시자 변수를 함께 포함할 수도 있음</li><li>지시자 변수는 NULL 값 등을 표현할 경우에 사용될 수 있음</li><li>USING 절의 변수는 배열 변수를 사용할 수도 있음</li><li>이때 USING 절 내의 모든 변수가 배열 변수이어야 하며 같은 크기를 가져야 함</li></ul></td></tr><tr><td>host_var</td><td>입력 변수로 사용될 호스트 변수</td></tr><tr><td>ind_var</td><td>지시자 변수</td></tr></tbody></table>

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

{% hint style="info" %}
참고

1. tbESQL/C 프로그램에서의 데이터 타입 변환은 “[데이터 타입](/tibero-manuals/7.2.6.manuals/tbesql-c-guide/data-types.md)”을 참고합니다.
2. 지시자 변수에 대해서는 “[지시자](/tibero-manuals/7.2.6.manuals/tbesql-c-guide/data-types.md#undefined-7)”를 참고합니다.
   {% endhint %}

#### **사용 예제**

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

```
'UPDATE EMP SET SALARY = SALARY * 1.05 WHERE DEPTNO = :deptno'
```

```
'INSERT INTO EMP(ENAME, DEPTNO) VALUES (:ename, :deptno)'
```

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

```
char sql_var[128];
double sal_ratio; int deptno;
...
strcpy(sql_var, "UPDATE EMP SET SALARY = SALARY * :sal_ratio" 
    "WHERE DEPTNO = :deptno");
EXEC SQL PREPARE update_emp FROM :sql_var;
...

sal_ratio = 1.05; deptno = 5;
EXEC SQL EXECUTE update_emp USING :sal_ratio, :deptno;

sal_ratio = 1.08; deptno = 6;
EXEC SQL EXECUTE update_emp USING :sal_ratio, :deptno;
```

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

### 방법 **3**

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

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

1. **PREPARE 단계**

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

2. **DECLARE 단계**

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

```
DECLARE cursor_name [SCROLL] CURSOR FOR stmt_name
```

<table><thead><tr><th width="171">항목</th><th>설명</th></tr></thead><tbody><tr><td>cursor_name</td><td><ul><li>선언하는 커서의 이름</li><li>커서 이름은 변수가 아니며 DECLARE 내에서만 선언됨</li><li>DECLARE를 이용해 여러 개의 커서를 선언한 경우에 OPEN, FETCH, CLOSE 등의 문장에서 하나의 커서를 지정하기 위하여 사용</li></ul></td></tr><tr><td>stmt_name</td><td>PREPARE를 통해 준비된 SELECT 문장의 이름</td></tr></tbody></table>

3. **OPEN 단계**

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

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

```
OPEN cursor_name [USING :host_var[:ind_var], ...]
```

<table><thead><tr><th width="164">항목</th><th>설명</th></tr></thead><tbody><tr><td>cursor_name</td><td>OPEN을 통해서 열 커서의 이름</td></tr><tr><td>USING</td><td>SELECT 문장의 입력 변수로 사용될 변수를 나열할 때 사용</td></tr><tr><td>host_var</td><td>입력 변수로 사용될 호스트 변수</td></tr><tr><td>ind_var</td><td>지시자 변수</td></tr></tbody></table>

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

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

```
EXEC SQL PREPARE stmt1 FROM :sql_var1; 
EXEC SQL DECLARE cursor1 FOR stmt1; 
EXEC SQL PREPARE stmt1 FROM :sql_var2; 
EXEC SQL OPEN cursor1;
```

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

4. **FETCH** 단계

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

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

```
FETCH [ FIRST | LAST | PRIOR | NEXT | CURRENT | RELATIVE offset
    | ABSOLUTE offset ] cursor_name INTO :host_var[:ind_var], ...
```

커서 이름 바로 앞에 있는 옵션은 액세스하려는 로우의 위치를 의미합니다. 스크롤 가능 커서에 대한 설명과 각 옵션의 의미는 “[스크롤 가능 커서](/tibero-manuals/7.2.6.manuals/tbesql-c-guide/basic-programming.md#undefined-12)”를 참고합니다.

5. **CLOSE** 단계

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

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

```
CLOSE cursor_name
```

<table><thead><tr><th width="236">항목</th><th>설명</th></tr></thead><tbody><tr><td>cursor_name</td><td>CLOSE를 통해서 닫을 커서의 이름</td></tr></tbody></table>

#### **사용 예제**

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

```
'SELECT ENAME, ADDR, SALARY FROM EMP WHERE DEPTNO = 5'
```

```
'SELECT DNAME, LOC FROM DEPT WHERE DEPTNO = :deptno'
```

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

```
char sql_var[128]; 
int deptno; 
VARCHAR ename[24];
double salary;
VARCHAR addr[36];

...
strcpy(sql_var, "SELECT ENAME, SALARY, ADDR" 
        "FROM EMP WHERE DEPTNO = :deptno");
EXEC SQL PREPARE select_stmt FROM :sql_var;
EXEC SQL DECLARE emp_cursor CURSOR FOR select_stmt;
...
deptno = 5;
EXEC SQL OPEN emp_cursor USING :deptno; 
EXEC SQL WHENEVER NOT FOUND DO break;

while (1) {
    EXEC SQL FETCH emp_cursor INTO :ename, :salary, :addr;
    ...
}

EXEC SQL CLOSE emp_cursor;
```

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

### 방법 **4**

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

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

```
struct SQLDA
{
    long N;           /* 서술자 내의 항목의 개수*/
    char **V;         /* 실제 데이터가 들어가 버퍼 배열 변수 */
    long *L;          /* 버퍼의 길이를 나타내는 변수 */
    short *T;         /* 버퍼의 타입을 나타내는 변수 */
    short *I;         /* 지시자 변수를 위한 공간 */
    long F;           /* DESCRIBE에 의해서 발견된 변수의 개수 */
    char **S;         /* 변수의 이름을 저장하기 위한 배열 변수 */
    short *M;         /* 변수 이름의 최대길이를 저장하는 변수 */
    short *C;         /* 변수 이름의 현재 값을 저장하는 변수 */
    char *X;          /* 지시자 변수 이름을 위한 배열 */
    short *Y;         /* 지시자 변수이름의 최대 길이 */
    short *Z;         /* 지시자 변수 이름의 현재 길이 */
}
```

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

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

<table><thead><tr><th width="249">External Type</th><th width="131">Type Code</th><th>C type</th></tr></thead><tbody><tr><td>VARCHAR2</td><td>1</td><td>char[n]</td></tr><tr><td>NUMBER</td><td>2</td><td>char[n] ( n &#x3C; 22 )</td></tr><tr><td>INTEGER</td><td>3</td><td>int</td></tr><tr><td>FLOAT</td><td>4</td><td>float</td></tr><tr><td>STRING</td><td>5</td><td>char[n+1]</td></tr><tr><td>VARNUM</td><td>6</td><td>char[n] ( n &#x3C; 22 )</td></tr><tr><td>DECIMAL</td><td>7</td><td>float</td></tr><tr><td>LONG</td><td>8</td><td>char[n]</td></tr><tr><td>BINARY_FLOAT</td><td>21</td><td>float</td></tr><tr><td>BINARY_DOUBLE</td><td>22</td><td>double</td></tr><tr><td>VARCHAR</td><td>9</td><td>char[n+2]</td></tr><tr><td>ROWID</td><td>11</td><td>char[n]</td></tr><tr><td>DATE</td><td>12</td><td>char[n]</td></tr><tr><td>VARRAW</td><td>15</td><td>char[n]</td></tr><tr><td>RAW</td><td>23</td><td>unsigned char[n]</td></tr><tr><td>LONG RAW</td><td>24</td><td>unsigned char[n]</td></tr><tr><td>UNSIGNED</td><td>68</td><td>unsigned int</td></tr><tr><td>DISPLAY</td><td>91</td><td>char[n]</td></tr><tr><td>LONG VARCHAR</td><td>94</td><td>char[n+4]</td></tr><tr><td>LONG VARRAW</td><td>95</td><td>unsigned char[n+4]</td></tr><tr><td>CHAR</td><td>96</td><td>char[n]</td></tr><tr><td>CHARF</td><td>96</td><td>char[n]</td></tr><tr><td>CHARZ</td><td>97</td><td>char[n+1]</td></tr><tr><td>CLOB</td><td>112</td><td>Cloblocator</td></tr><tr><td>BLOB</td><td>113</td><td>Bloblocator</td></tr></tbody></table>

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

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

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

```
#include <sqlda.h> 
SQLDA *select_dp; 
SQLDA * bind_dp;

char *id = "T002"; 
char *cd = "B001";

char *stmt = "select teller_name from teller where teller_id=:id 
                and\ branch_cd=:cd";
```

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

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

```
bind_dp = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, (size_t)5, 4);
select_dp = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, (size_t)5, (size_t)0);
```

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

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

```
select_dp->N = 3;
bind_dp->N = 3;
```

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

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

```
EXEC SQL PREPARE S1 FROM :stmt;
```

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

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

```
EXEC SQL DECLARE C1 CURSOR FOR S1;
```

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

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

```
EXEC SQL DESCRIBE BIND VARIABLES FOR S1 INTO bind_dp;
```

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

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

```
bind_dp->N = bind_dp->F
```

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

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

```
bind_dp->V[0] = id;
bind_dp->L[0] = strlen(id); 
bind_dp->T[0] = 5;
bind_dp->V[1] = cd;
bind_dp->L[1] = strlen(cd); 
bind_dp->T[1] = 5;
```

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

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

```
EXEC SQL OPEN C1 USING DESCRIPTOR bind_dp;
```

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

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

```
EXEC SQL DESCRIBE SELECT LIST FOR S1 INTO select_dp;
```

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

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

```
select_dp->N = select_dp->F;
```

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

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

```
select_dp->V[0] = malloc(select_dp->L[0] + 1); 
memset(select_dp->V[0], 0, select_dp->L[0] + 1);
```

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

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

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

```
EXEC SQL FETCH C1 USING DESCRIPTOR select_dp;
```

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

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

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

```
free(select_dp->V[0]);

SQLSQLDAFree(SQL_SINGLE_RCTX, select_dp); 
SQLSQLDAFree(SQL_SINGLE_RCTX, bind_dp);
```

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

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

```
EXEC SQL CLOSE C1;
```

#### **사용 예제**

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

```
char *stmt = "select teller_id, teller_name from teller where branch_cd=:cd 
                \ order by teller_id";
char *cd = "B001"; 
int count = 2;

SQLDA *bind_dp; 
SQLDA *select_dp;

EXEC SQL WHENEVER SQLERROR
                DO printf("file: %s, line: %d, error code: %d\n", FILE , LINE , 
                           sqlca.sqlcode);
                           
bind_dp = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, (size_t)5, 4);
select_dp = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, (size_t)5, (size_t)0);

bind_dp->N = MAX_ITEMS; 
select_dp->N = MAX_ITEMS;

/* prepare */
EXEC SQL PREPARE S1 FROM :stmt;

/* declare cursor */
EXEC SQL DECLARE C2 CURSOR FOR S1;

/* describe bind parameters */
EXEC SQL DESCRIBE BIND VARIABLES FOR S1 INTO bind_dp;

bind_dp->N = bind_dp->F;

/* bind parameters */ 
bind_dp->V[0] = cd;
bind_dp->L[0] = strlen(cd) + 1;
 bind_dp->T[0] = 5;
 
/* open cursor */
EXEC SQL OPEN C2 USING DESCRIPTOR bind_dp;

/* describe select list */
EXEC SQL DESCRIBE SELECT LIST FOR S1 INTO select_dp;

/* allocate bind col buffer */ 
select_dp->N = select_dp->F;
select_dp->V[0] = malloc(select_dp->L[0] * 2); 
memset(select_dp->V[0], 0, select_dp->L[0] * 2); 
select_dp->V[1] = malloc(select_dp->L[1] * 2); 
memset(select_dp->V[1], 0, select_dp->L[1] * 2);
select_dp->T[1] = 5;

/* fetch */
EXEC SQL FOR :count FETCH C2 USING DESCRIPTOR select_dp; 

EXEC SQL CLOSE C2;

/* free sqlda */ 
SQLSQLDAFree(SQL_SINGLE_RCTX, bind_dp); 
SQLSQLDAFree(SQL_SINGLE_RCTX, select_dp);

EXEC SQL COMMIT RELEASE;
```
