Preloader image
DDD

유용한 TIP

대용량 SQL EXEC 를 while 문으로 저장 후 for 문으로 속도 튜닝

작성자 관리자 (admin)
조회수 35
입력일 2025-11-02 10:43:04

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlca.h>

/* ==== 소스 데이터 구조체 ==== */
typedef struct {
    int id;
    char name[50];
    double amount;
} SRC_DATA;

/* ==== 상수 정의 ==== */
#define FETCH_SIZE   1000       /* FETCH 단위 */
#define COMMIT_SIZE  10000      /* COMMIT 단위 */
#define MAX_ROWS     100000000  /* 최대 누적 가능 데이터 (100GB면 메모리 주의) */

/* ==== 호스트 변수 ==== */
EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR src_user[50];
    VARCHAR tgt_user[50];
EXEC SQL END DECLARE SECTION;

/* ==== 전역 변수 ==== */
SRC_DATA *src_data = NULL;
long total_cnt = 0;
long commit_cnt = 0;
long alloc_size = 0;

/* ==== 함수 선언 ==== */
void sql_error(const char *msg);

int main() {
    int i;
    long rows;

    /* ==== DB 접속 ==== */
    strcpy((char *)src_user.arr, "SRCUSER/PASSWORD@SRCDB");
    src_user.len = strlen((char *)src_user.arr);
    strcpy((char *)tgt_user.arr, "TGTUSER/PASSWORD@TGTDB");
    tgt_user.len = strlen((char *)tgt_user.arr);

    EXEC SQL CONNECT :src_user;
    if (sqlca.sqlcode != 0) sql_error("Source CONNECT");

    EXEC SQL AT :tgt_user CONNECT :tgt_user;
    if (sqlca.sqlcode != 0) sql_error("Target CONNECT");

    printf("Connected both DBs.\n");

    /* ==== 커서 선언 ==== */
    EXEC SQL DECLARE src_cur CURSOR FOR
        SELECT id, name, amount FROM big_table;

    EXEC SQL OPEN src_cur;

    /* ==== 메모리 초기화 ==== */
    alloc_size = FETCH_SIZE;
    src_data = (SRC_DATA *)malloc(sizeof(SRC_DATA) * alloc_size);
    if (!src_data) {
        printf("Memory allocation error.\n");
        exit(1);
    }

    /* ==== 1단계: FETCH & APPEND ==== */
    while (1) {
        SRC_DATA temp[FETCH_SIZE];

        EXEC SQL FETCH src_cur INTO :temp;
        rows = sqlca.sqlerrd[2];
        if (rows == 0) break;

        /* ==== 누적 저장 ==== */
        if (total_cnt + rows > alloc_size) {
            alloc_size *= 2; /* 2배씩 확장 */
            src_data = (SRC_DATA *)realloc(src_data, sizeof(SRC_DATA) * alloc_size);
            if (!src_data) {
                printf("Memory realloc error.\n");
                exit(1);
            }
        }

        memcpy(&src_data[total_cnt], temp, sizeof(SRC_DATA) * rows);
        total_cnt += rows;

        if (total_cnt % (FETCH_SIZE * 100) == 0)
            printf("Fetched %ld rows so far...\n", total_cnt);
    }

    EXEC SQL CLOSE src_cur;
    EXEC SQL COMMIT;
    printf("All FETCH completed. Total rows = %ld\n", total_cnt);

    /* ==== 2단계: INSERT LOOP ==== */
    for (i = 0; i < total_cnt; i++) {
        EXEC SQL AT :tgt_user
            INSERT INTO temp_table (id, name, amount)
            VALUES (:src_data[i].id, :src_data[i].name, :src_data[i].amount);

        commit_cnt++;
        if (commit_cnt >= COMMIT_SIZE) {
            EXEC SQL AT :tgt_user COMMIT;
            printf("Committed %ld rows...\n", i + 1);
            commit_cnt = 0;
        }
    }

    /* ==== 잔여 데이터 COMMIT ==== */
    if (commit_cnt > 0) {
        EXEC SQL AT :tgt_user COMMIT;
        printf("Final commit completed.\n");
    }

    EXEC SQL AT :tgt_user DISCONNECT;
    EXEC SQL DISCONNECT;
    free(src_data);

    printf("All data transferred successfully.\n");
    return 0;
}

/* ==== 에러 처리 ==== */
void sql_error(const char *msg) {
    printf("SQL ERROR: %s\n", msg);
    printf("SQLCODE: %ld\n", sqlca.sqlcode);
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK;
    exit(1);
}