정리

SQLite - 쿼리 실행

저장소/VC++

닥치고 샘플 ㄱㄱ~

// Insert에 사용될 데이터 구조체
typedef struct _my_value
{
// 이것 저것 있음

} MY_VALUE, *PMY_VALUE;

sqlite3* g_pDB = NULL;

int TestFunc_Open(const char* pszDBFilePath);
int TestFunc_Close();
int TestFunc_QueryExec1(const char* pszTableName,
    const char* pszQuery,
    sqlite3_callback xCallback);
int TestFunc_QueryExec2(const char* pszTableName, const char* pszQuery);
int TestFunc_QE_Insert(const char* pszTableName,
 int nColumnCount,
 std::list<PMY_VALUE>* plstValues);


int TestFunc_Open(const char* pszDBFilePath)
{
// 파라미터검증 생략

int nRet = SQLITE_OK;
nRet = sqlite3_open_v2(pszDBFilePath,
&g_pDB,
SQLITE_OPEN_READWRITE,
NULL);
if(SQLITE_OK != nRet)
{
// 이쯤에서 적절한 에러처리.
}

return 0;
}

int TestFunc_Close()
{
if(NULL != g_pDB)
{
 int nRet = SQLITE_OK;
nRet = sqlite3_close(g_pDB);
if(SQLITE_OK != nRet)
{
// 아이고~ 실패입니다요! 에러처리!
}
}

return 0;
}

/*
 sqlite3_exec 함수를 사용한 방식
 sqlite3_exec 함수는 내부에서 아래와 같은 과정으로 처리됨.
 (sqlite3_prepare -> sqlite3_step -> sqlite3_finalize)
 단일 명령 처리에는 사용하기 편하지만
 빠르고 연속적인 쿼리를 처리하기엔 오버헤드를 무시할 수 없음.
*/
int TestFunc_QueryExec1(const char* pszTableName,
    const char* pszQuery,
    sqlite3_callback xCallback)
{
// 파라미터 및 이것저것 검증 생략

int nRet = SQLITE_OK;
char* pszErrMsg = NULL;
nRet = sqlite3_exec(g_pDB, pszQuery, xCallback, NULL, &pszErrMsg);
if(SQLITE_OK != nRet)
{
// 실패 원인 확인 1. Code
int nErrCode = sqlite3_errcode(g_pDB);

// 실패 원인 확인 2. Message
const char* pszErrMessage = sqlite3_errmsg(g_pDB);

// 실패 원인 확인 3. 반환된 Error Message
// pszErrMsg <- 요거요거
// 반환된 Error Message는 알아볼거 알아보고 나중에
// sqlite3_free를 통해 메모리 해제를 해줘야 한다고 한다.
// (참고로 잘못된 대상으로 sqlite3_free를 사용하면 뻗어버린다!)
sqlite3_free((void*)pszErrMsg);
}

return 0;
}

/*
 sqlite3_prepare 함수를 사용한 방식
 sqlite3_exec 함수를 사용한 것보다 귀찮긴 하지만
 sqlite3_bind_xxx 함수와 sqlite3_reset 함수를 활용하여 쿼리의 재사용성을 높일 수 있다.
 작업의 끝은 sqlite3_finalize 함수 호출로 막을 내린다.
*/
int TestFunc_QueryExec2(const char* pszTableName, const char* pszQuery)
{
 // 파라미터 및 이것저것 검증 생략

int nRet = SQLITE_OK;
sqlite3_stmt* pStmt = NULL;

// 실행할 쿼리를 준비하고
nRet = sqlite3_prepare(g_pDB, pszQuery, -1, &pStmt, NULL);
if(SQLITE_OK == nRet)
{
// 준비된 쿼리를 실행한다.
nRet = sqlite3_step(pStmt);
while(SQLITE_ROW == nRet)
{
// 뭐 필요한 작업 있으면 여기서
nRet = sqlite3_step(pStmt);
}

nRet = sqlite3_finalize(pStmt);
}
else
{
// 에러처리
}

return 0;
}

int TestFunc_QE_Insert(const char* pszTableName,
 int nColumnCount,
 std::list<PMY_VALUE>* plstValues)
{
// 파라미터 및 이것저것 검증 생략

// 대량의 INSERT를 처리할 경우 BEGIN ~ COMMIT 을 쓰면 속도가 빠르다고 함.
// 임시파일 어쩌구 하던데 자세히 모르니까 나중에 다시 찾아보도록...
int nRet = SQLITE_OK;

nRet = sqlite3_exec(g_pDB, "BEGIN;", NULL, NULL, NULL);

char szBind[100] = "?";
for(int i = 0 ; i < nColumnCount ; i++)
{
strncat_s(szBind, 100, ", ?", _TRUNCATE);
}

char szQuery[512] = {0,};
strncpy_s(szQuery, 512, "INSERT INTO ", _TRUNCATE);
strncat_s(szQuery, 512, pszTableName, _TRUNCATE);
strncat_s(szQuery, 512, " VALUES(", _TRUNCATE);
strncat_s(szQuery, 512, szBind, _TRUNCATE);
strncat_s(sqQuery, 512, ");", _TRUNCATE);

sqlite3_stmt* pStmt = NULL;
nRet = sqlite3_prepare(g_pDB, szQuery, -1, &pStmt, NULL);
if(SQLITE_OK == nRet)
{
std::list<PMY_VALUE>::iterator iter;

for(iter = plstValues->begin() ; iter != plstValues->end() ; ++iter)
{
PMY_VALUE pValue = *iter;

nRet = sqlite3_reset(pStmt);

// Column 수에 맞게, 데이터 형식에 맞게 sqlite3_bind 함수를 사용하여
// 필요 값을 넣는다.
sqlite3_bind_int(pStmt, 1, pValue->xxx);
sqlite3_bind_double(pStmt, 2, pValue->aaa);
sqlite3_bind_text(pStmt, 3, pValue->bbb, -1, SQLITE_STATIC);

// 완성된 쿼리 실행
nRet = sqlite3_step(pStmt);
whlie(SQLITE_ROW == nRet)
{
nRet = sqlite_step(pStmt);
}
}

nRet = sqlite3_finalize(pStmt);
}

nRet = sqlite3_exec(g_pDB, "COMMIT;", NULL, NULL, NULL);

return 0;
}