콘텐츠로 이동

5. Array Binding과 Array Fetch#

이 장은 여러 건의 데이터를 한꺼번에 처리하는 방법인 array binding과 array fetch에 대해서 설명한다.

개요#

Array Binding은 한 SQL문의 각 파라미터에 대해 배열의 값들을 한꺼번에 서버로 전달하는 방법이다. 이 방법을 사용하면 네트워크 전송 횟수가 줄어들어서 속도 향상을 얻을 수 있다.

Array Fetch는 질의 결과 집합의 행을 클라이언트에서 지정한 배열의 크기만큼 한꺼번에 서버로부터 가져오는 방법이다. 이 방법도 Array Binding과 마찬가지로 네트워크 전송 횟수를 줄이므로 속도 향상을 얻을 수 있다.

다음 그림은 Array Binding을 간략하게 도식화한 것이다. 네트워크 전송 횟수가 줄어들어 짧은 시간에 더 많은 데이터를 전송할 수 있음을 알 수 있다.

Array Binding과 Array Fetch에 대한 더 자세한 설명은 CLI User's Manual를 참고한다.

ALTIBASE_BIND 구조체 사용하기#

Array 방식으로 응용 프로그램을 작성하려면, 원래 방식과 마찬가지로 ALTIBASE_BIND 구조체와 함께 altibase_stmt_bind_param() 또는 altibase_stmt_bind_result() 함수를 이용해야 한다.

altibase_stmt_set_array_bind() 또는 altibase_stmt_set_array_fetch() 를 통해서 배열의 크기 (Array Size)가 설정되었으면, ACI 라이브러리는 array 방식을 사용하는 것이라고 판단한다. 또한, ACI 라이브러리는 ALTIBASE_BIND 구조체가 지정된 array방식에 맞춰서 설정되었을 것이라고 간주한다. 따라서, array 방식을 사용할 때는 ALTIBASE_BIND 구조체의 buffer와 buffer_length 멤버를 주의해서 설정해야 한다.

buffer#

ALTIBASE_BIND 구조체의 buffer 멤버에는 설정된 배열 크기만큼의 배열 변수가 지정되어야 한다.

다음은 ALTIBASE_BIND_INTEGER 타입의 5개의 요소를 갖는 배열을 바인딩하는 소스 코드 예제이다.

#define ARRAY_SIZE 5

/* ... omit ... */

int           int_dat[ARRAY_SIZE];
ALTIBASE_BIND bind;

/* ... omit ... */

bind.buffer_type   = ALTIBASE_BIND_INTEGER;
bind.buffer        = int_dat;

만약 buffer에 지정한 변수의 배열 크기가 설정된 배열 크기보다 크다면, 설정된 배열 크기를 제외한 나머지 요소는 무시될 것이다.

buffer_length#

바인딩할 값이 ALTIBASE_BIND_STRING 처럼 크기가 정해져 있지 않는 타입이라면, ALTIBAE_BIND 구조체의 buffer_length 멤버 값도 정확하게 설정되어야 한다.

다음은 최대 크기가 50인 CHAR 칼럼을 위해서 5개의 요소를 갖는 배열을 바인딩하는 소스 코드 예제이다.

#define ARRAY_SIZE 5
#define STR_SIZE   50

/* ... omit ... */

char           str_dat[ARRAY_SIZE][STR_SIZE];
ALTIBASE_BIND bind;

/* ... omit ... */

bind.buffer_type   = ALTIBASE_BIND_STRING;
bind.buffer        = str_dat;
bind.buffer_length = STR_SIZE;

ALTIBASE_BIND_INTEGER 처럼 크기가 정해져 있는 타입의 경우에는 buffer_length 값이 버퍼의 크기로 설정될 필요가 없다. 버퍼가 고정된 크기의 타입일 경우 buffer_length가 0으로 초기화 되어 있으면, ACI 라이브러리는 데이타를 담는데 필요한 크기만큼을 버퍼에 할당했다고 간주한다.

Array Binding#

Array Binding은 한 SQL문의 각 파라미터에 대해 배열 변수를 바인딩하여 한꺼번에 배열의 값들을 서버로 전달하는 방법이다. 이 방법은 네트워크 전송 횟수를 줄이므로 속도 향상을 얻을 수 있다.

예제

#define ARRAY_SIZE  2
#define PARAM_COUNT 2
#define STR_SIZE    50
#define QSTR        "INSERT INTO t1 VALUES (?, ?)"

int           int_dat[ARRAY_SIZE];
char          str_dat[ARRAY_SIZE][STR_SIZE];
ALTIBASE_LONG length[PARAM_COUNT][ARRAY_SIZE];

ALTIBASE      altibase;
ALTIBASE_STMT stmt;
ALTIBASE_BIND bind[PARAM_COUNT];
int           rc;
int           i;

/* ... omit ... */

int_dat[0] = 1;
int_dat[1] = 2;
strcpy(str_dat[0], "test1");
strcpy(str_dat[1], "test2");

length[0][0] = sizeof(int);
length[0][1] = sizeof(int);
length[1][0] = strlen(str_dat[0]);
length[1][1] = ALTIBASE_NTS;

memset(bind, 0, sizeof(bind));

bind[0].buffer_type   = ALTIBASE_BIND_INTEGER;
bind[0].buffer        = int_dat;
bind[0].length        = length[0];

bind[1].buffer_type   = ALTIBASE_BIND_STRING;
bind[1].buffer        = str_dat;
bind[1].buffer_length = STR_SIZE;
bind[1].length        = length[1];

stmt = altibase_stmt_init(altibase);
/* ... check return value ... */

rc = altibase_stmt_prepare(stmt, QSTR);
/* ... check return value ... */

rc = altibase_stmt_set_array_bind(stmt, ARRAY_SIZE);
/* ... check return value ... */

rc = altibase_stmt_bind_param(stmt, bind);
/* ... check return value ... */

rc = altibase_stmt_execute(stmt);
/* ... check return value ... */

printf("processed : %d\n", altibase_stmt_processed(stmt));
for (i = 0; i < ARRAY_SIZE; i++)
{
    printf("%d status : %d\n", i, altibase_stmt_status(stmt)[i]);
}

Array Fetch#

Array Fetch는 질의 결과 집합의 행을 클라이언트에서 지정한 배열의 크기만큼 한꺼번에 서버로부터 가져오는 방법이다.

Array Fetch를 사용할 때는 altibase_stmt_fetch() 수행 시 가져온 행의 개수가 설정된 배열의 크기보다 적을 수 있다. 따라서 altibase_stmt_fetch() 수행 후에는 altibase_stmt_fetched()를 이용해서 실제로 받아온 행의 개수를 확인해야 한다. altibase_stmt_fetched()가 반환한 값이 설정된 배열의 크기보다 작으면, 더 이상 가져올 행이 없음을 의미한다.

예제

#define ARRAY_SIZE  2
#define FIELD_COUNT 2
#define STR_SIZE    50
#define QSTR        "SELECT * FROM t1"

int           int_dat[ARRAY_SIZE];
char          str_dat[ARRAY_SIZE][STR_SIZE];
ALTIBASE_LONG length[FIELD_COUNT][ARRAY_SIZE];
ALTIBASE_BOOL is_null[FIELD_COUNT][ARRAY_SIZE];

ALTIBASE      altibase;
ALTIBASE_STMT stmt;
ALTIBASE_BIND bind[FIELD_COUNT];
int           rc;
int           i;
int           row;
int           fetched;
int           status;

/* ... omit ... */

stmt = altibase_stmt_init(altibase);
/* ... check return value ... */

rc = altibase_stmt_prepare(stmt, QSTR);
/* ... check return value ... */

rc = altibase_stmt_execute(stmt);
/* ... check return value ... */

rc = altibase_stmt_set_array_fetch(stmt, ARRAY_SIZE);
/* ... check return value ... */

memset(bind, 0, sizeof(bind));

bind[0].buffer_type   = ALTIBASE_BIND_INTEGER;
bind[0].buffer        = int_dat;
bind[0].length        = length[0];
bind[0].is_null       = is_null[0];

bind[1].buffer_type   = ALTIBASE_BIND_STRING;
bind[1].buffer        = str_dat;
bind[1].buffer_length = STR_SIZE;
bind[1].length        = length[1];
bind[1].is_null       = is_null[1];

rc = altibase_stmt_bind_result(stmt, bind);
/* ... check return value ... */

do
{
    rc = altibase_stmt_fetch(stmt);
    if (rc == ALTIBASE_NO_DATA)
    {
        break;
    }
    if (ALTIBASE_NOT_SUCCEEDED(rc))
    {
        /* ... error handling ... */
        break;
    }

    fetched = altibase_stmt_fetched(stmt);
    for (i = 0; i < fetched; i++)
    {
        printf("row %d : ", row);
        status = altibase_stmt_status(stmt)[i];
        if (ALTIBASE_ROW_SUCCEEDED(status))
        {
            if (is_null[0][i] == ALTIBASE_TRUE)
            {
                printf("{null}");
            }
            else
            {
                printf("%d", int_dat[i]);
            }
            printf(", ");
            if (is_null[1][i] == ALTIBASE_TRUE)
            {
                printf("{null}");
            }
            else
            {
                printf("(%d) %s", length[1][i], str_dat[i]);
            }
        }
        else
        {
            printf("{status:%d}", status);
        }
        printf("\n");
        row++;
    }
} while (fetched == ARRAY_SIZE);