세상을 이롭게

[SQLITE] Bulk Insert 본문

SQLite

[SQLITE] Bulk Insert

2022. 1. 17. 14:01

x86 기준이다. x64도 마찬가지이므로 따라하셔도 무방하다.

#include "sqlite3.h"
#include <string> //string
#include <iostream> // std::
#define DATABASE "C:\\Users\\user\\Desktop\\sqlite_x86Test\\Test.db3"
#define CREATETABLE_DATA "CREATE TABLE IF NOT EXISTS DATA (ID INTEGER PRIMARY KEY AUTOINCREMENT, TIME datetime);"
#define INSERT_DATA "INSERT INTO DATA VALUES (NULL, @TIME);"
#define SELECT_COUNT "SELECT COUNT(*) FROM DATA;"
#define BUFFER_SIZE 256

using namespace std;

sqlite3* db = NULL;
sqlite3_stmt* res = NULL;
char* err_msg = 0;
const char* tail = 0;
int rc = 0;

struct st_DATA {
    string time;
};

int callback(void* NotUsed, int argc, char** argv, char** azColName)
{
    NotUsed = 0;
    for (int i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

int Count() {
    rc = sqlite3_exec(db, SELECT_COUNT, callback, 0, &err_msg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "Failed to Data Count data\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        return 1;
    }
}

int OpenDataBase() {
    rc = sqlite3_open(DATABASE, &db);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }
    else {
        cout << DATABASE << " Open database\n";
    }
}

int CloseDataBase() {
    sqlite3_close(db);
    sqlite3_free(err_msg);
    return 0;
}

int BulkInsertDB() {
    res = NULL;
    rc = sqlite3_exec(db, CREATETABLE_DATA, NULL, NULL, &err_msg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "Failed to create table\n");
        fprintf(stderr, "SQL error: %s\n", err_msg);
        sqlite3_free(err_msg);
    }
    else
    {
        fprintf(stdout, "Table Data created successfully\n");
    }

    sqlite3_prepare_v2(db, INSERT_DATA, BUFFER_SIZE, &res, &tail);
    sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &err_msg);

    while (Bulk Insert)
    {
        //원하는 로직을 적어줍니다.
        sqlite3_bind_text(res, 1, st_DATA.time.c_str(), -1, SQLITE_TRANSIENT);
        sqlite3_step(res);
        sqlite3_reset(res);
    }
    sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &err_msg);
    sqlite3_finalize(res);
    return 0;
}


int main(void)
{
    OpenDataBase();
    BulkInsertDB();
    Count();
    CloseDataBase();
    return 0;
}
 
1. BulK Insert 시 트랜젝션을 사용하였다.
INSERT 구문을 호출할때마다 트랜잭션이 이뤄지기 때문에 직접 트랜잭션으로 묶어주어 성능을 향상 시켰다.
BEGIN, BEGIN TRANSACTION 으로 트랜잭션을 시작한다고 알려주고, END,  END TRANSACTION 으로
트랜잭션의 끝을 알려주자. 같은 작업을 하는 함수, 반복문단위로 계속적인 호출이 있는 경우 사용하자.

2.  sqlite3_exec 함수 대신, sqlite3_prepare_v2 함수를 사용하였다. 
sqlite3_step(res); 함수는 sqlite3_prepare* 함수로 컴파일된 쿼리를 실행하게된다. 
결과값이 보고 싶다면 int step = sqlite3_step(res); 를 통해 받고, if (SQLITE_ROW == step) 조건문으로 여부를 확인 할 수 있다.

3. sqlite3_reset(res); 사용하였다.
statement를 단 한번만 prepare하고 필요시 reset하여 재사용하였다. (res 를 이야기함.)


결론
sqlite_bind_text 와 sqlite3_res, sqlite3_reset 를 사용함으로 재사용을 통해 시간을 단축 시킬 수 있었다.
sqlite_bind_text 에서 한글 사용시 꼭 UTF-8을 맞춰주어야 한다.

만약 한글이 CP949, unicode, ANSI ASCII 등 다른 인코딩으로 바인딩 될 시엔
Create Table 시 TEXT로 되어있던 타입이 BLOB 타입으로 바뀌어 들어가게 된다.
DB Browser 에서 깨져 보이는것은 덤이다.

아래는 많은 도움을 받은 블로그이다.

'SQLite' 카테고리의 다른 글

SQL error: attempt to write a readonly database  (0) 2022.07.11
[SQLITE] x86 개발환경 셋팅  (0) 2022.01.17