SQLITE 操作入門

SQLITE 操作入門  


sqlite 提供的是一些 C 函數接口,你可以用這些函數操作數據庫。通過使用這些接 口,傳遞一些標準 sql 語句(以 char * 類型)給 sqlite 函數, sqlite 就會爲你操作數據庫。  

sqlite 跟 MS  access 一樣是文件型數據庫,就是說,一個數據庫就是一個文件,此 數據庫裏可以建立很多的表,可以建立索引、觸發器等等,但是,它實際上得到的就是一個文件。備份這個文件就備份了整個數據庫。  

sqlite 不需要任何數據庫引擎,這意味着如果你需要 sqlite 來保存一些用戶數據,甚至都不需要安裝數據庫 ( 如果你做個小軟件還要求人家必須裝了 sqlserver 才能運行,那也太黑心了 )   

下面開始介紹數據庫基本操作。  


1 基本流程  

 1 )關鍵 數據結構  

sqlite 裏最常用到的是 sqlite3 * 類型。從數據庫打開開始, sqlite 就要爲這個類型準備好內存,直到數據庫關閉,整個過程都需 要用到這個類型。當數據庫打開時開始,這個類型的變量就代表了你要操作的數據庫。下面再詳細介紹。  

 2 )打開 數據庫  

int sqlite3_open( 文件名 , sqlite3 ** );  

用這個函數開始數據庫操作。  

需要傳入兩個參數,一是數據庫文件名,比如: c:\\DongChunGuang_Database.db   

文件名不需要一定存在,如果此文件不存在, sqlite 會自動建立它。如果它存在,就嘗試把它當數據庫文件來打 開。  

sqlite3 ** 參數即前面提到的關鍵數據結構。這個結構底層細節如何,你 不要關它。  

函數返回值表示操作是否正確,如果是 SQLITE_OK 則表示操作正常。相關的返回值 sqlite 定義了一些宏。具體這些宏的含義可以參考 sqlite3.h 文件。裏面有詳細定義(順便說一下, sqlite3 的代碼註釋率自稱是非常高的,實際上也的確很高。只要你會 看英文, sqlite 可以讓你學到不少東西)。  

下面介紹關閉數據庫後,再給一段參考代碼。  

 3  關閉數據庫  

int sqlite3_close(sqlite3 *);  

前面如果用 sqlite3_open 開啓了一個數據庫,結尾時不要忘了用這個函數關閉數據庫。  

下面給段簡單的代碼:  

extern "C"  

{  

#include "./sqlite3.h"  

};  

int main( int , char** )  

{  

   sqlite3 * db = NULL; // 聲明sqlite 關鍵結構指針  

    int result;  

   

// 打開數據庫  

// 需要傳入 db 這個指針的指針,因爲 sqlite3_open 函數要爲這個指針分配內存,還要讓db 指針指向這個內存區  

   result = sqlite3_open( “ c:\\Dcg_database.db  , &db );  

   if( result != SQLITE_OK )  

   {  

    // 數據庫打開失敗  

return -1;  

}  

// 數據庫操作代碼  

//   

   

// 數據庫打開成功  

// 關閉數據庫  

sqlite3_close( db );  

return 0;  

}  

這就是一次數據庫操作過程。  

 


2 SQL 語句操作  


本節介紹如何用 sqlite 執行標準 sql 語法。  

   

 1 )執行 sql 語句  

int sqlite3_exec(sqlite3*, const char *sql, sqlite3_callback, void *,  char **errmsg );  

這就是執行一條 sql 語句的函數。  

 1 個參數不再說了,是前面 open 函數得到的指針。說了是關鍵數據結構。  

 2 個參數 const char *sql 是一條 sql 語句,以 \0 結尾。  

 3 個參數 sqlite3_callback 是回調,當這條語句執行之後, sqlite3 會去調用你提供的這個函數。(什麼是回調函數,自己找別的 資料學習)  

 4 個參數 void * 是你所提供的指針,你可以傳遞任何一個指針參數到這裏,這 個參數最終會傳到回調函數裏面,如果不需要傳遞指針給回調函數,可以填 NULL 。等下我們再看回調函數的寫法,以及這個參數的使用。  

 5 個參數 char ** errmsg 是錯誤信息。注意是指針的指針。 sqlite3 裏面有很多固定的錯誤信息。執行 sqlite3_exec 之後,執行失敗時可以查閱這個指針(直接 printf(  %s\n  ,errmsg) )得到一串字符串信息,這串信息告訴你錯在什麼地方。 sqlite3_exec 函數通過修改你傳入的指針的指針,把你提供的指針指向錯誤 提示信息,這樣 sqlite3_exec 函數外面就可以通過這個 char* 得到具體錯誤提示。  

說明:通常, sqlite3_callback 和它後面的 void * 這兩個位置都可以填 NULL 。填 NULL 表示你不需要回調。比如你做 insert 操作,做 delete 操作,就沒有必要使用回調。而當你做 select 時,就要使用回調,因爲 sqlite3 把數據查出來,得通過回調告訴你查出了什麼數據。  

 2  exec 的回調  

typedef int (*sqlite3_callback)( void *, int , char **, char **);  

你的回調函數必須定義成上面這個函數的類型。下面給個簡單的例子:  

//sqlite3 的回調函數         

// sqlite 每查到一條記錄,就調 用一次這個回調  

int LoadMyInfo( void * para, int n_column, char ** column_value, char ** column_name )  

{  

   //para 是你在 sqlite3_exec 裏傳入的 void * 參數  

   // 通過para 參數,你可以傳入一些特殊的指針(比如類指針、結構指針),然後在這裏面強制轉換成對應的類型(這裏面是void* 類型,必須強制轉換成你的類型纔可用)。然後操作這些數據  

   //n_column 是這一條記錄有多少個 字段 ( 即這條記錄有多少列)  

   // char ** column_value 是個關鍵值,查出來的 數據都保存在這裏,它實際上是個1 維數組(不要以爲是2 維數組),每一個元素都是一個 char * 值,是一個字段內容(用字符串來表示,以\0 結尾)  

   //char ** column_name 跟 column_value 是對應的,表示這個字段的字段名稱  

          

    // 這裏,我不使用 para 參數。忽略它的存在.  

   

    int i;  

printf( “ 記錄包含 %d 個字段\n ” , n_column );  

for( i = 0 ; i < n_column; i ++ )  

{  

    printf( “ 字段名:%s  ß > 字段值:%s\n ” ,  column_name[i], column_value[i] );  

}  

printf( “ ------------------\n “ );           

return 0;  

}  

   

int main( int , char ** )  

{  

    sqlite3 * db;  

    int result;  

    char * errmsg = NULL;  

   

    result = sqlite3_open( “ c:\\Dcg_database.db ” , &db );  

    if( result != SQLITE_OK )  

    {  

        // 數據庫打開失敗  

return -1;  

}  

   

// 數據庫操作代碼  

// 創建一個測試表,表名 叫 MyTable_1 ,有2 個字段: ID 和 name 。其中ID 是一個自動增加的類型,以後insert 時可以不去指定這個字段,它會自己從0 開始增加  

result = sqlite3_exec( db, “ create table MyTable_1( ID integer primary key autoincrement, name nvarchar(32) ) ” , NULL, NULL, errmsg );  

if(result != SQLITE_OK )  

{  

    printf( “ 創建表失敗,錯誤碼:%d ,錯誤原因:%s\n ” , result, errmsg );  

}  

   

// 插入一些記錄  

result = sqlite3_exec( db, “ insert into MyTable_1( name ) values ( ‘ 走路 ’ ) ” , 0, 0, errmsg );  

if(result != SQLITE_OK )  

{  

    printf( “ 插入記錄失敗,錯誤碼:%d ,錯誤原因:%s\n ” , result, errmsg );  

}  

   

result = sqlite3_exec( db, “ insert into MyTable_1( name ) values ( ‘ 騎單車 ’ ) ” , 0, 0, errmsg );  

if(result != SQLITE_OK )  

{  

    printf( “ 插入記錄失敗,錯誤碼:%d ,錯誤原因:%s\n ” , result, errmsg );  

}  

   

result = sqlite3_exec( db, “ insert into MyTable_1( name ) values ( ‘ 坐汽車 ’ ) ” , 0, 0, errmsg );  

if(result != SQLITE_OK )  

{  

    printf( “ 插入記錄失敗,錯誤碼:%d ,錯誤原因:%s\n ” , result, errmsg );  

}  

   

// 開始查詢數據庫  

result = sqlite3_exec( db, “ select * from MyTable_1 ” , LoadMyInfo, NULL, errmsg );  

   

// 關閉數據庫  

sqlite3_close( db );  

return 0;  

}  

   

 過上面的例子,應該可以知道如何打開一個數據庫,如何做數 據庫基本操作。  

有這些 知識,基本上可以應付很多數據庫操作了。  

   

 3 )不使 用回調查詢數據庫  

上面介紹的 sqlite3_exec 是使用回調來執行 select 操作。還有一個方法可以直接查詢而不需要回調。但是,我個 人感覺還是回調好,因爲代碼可以更加整齊,只不過用回調很麻煩,你得聲明一個函數,如果這個函數是類成員函數,你還不得不把它聲明成 static 的(要問爲什麼?這又是 C++ 基礎了。 C++ 成員函數實際上隱藏了一個參數: this  C++ 調用類的成員函數的時候,隱含把類指針當成函數的第一個參 數傳遞進去。結果,這造成跟前面說的 sqlite 回調函數的參數不相符。只有當把成員函數聲明成 static 時,它纔沒有多餘的隱含的 this 參數)。  

雖然回調顯得代碼整齊,但有時候你還是想要非回調的 select 查詢。這可以通過 sqlite3_get_table 函數做到。  

int sqlite3_get_table(sqlite3*, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg );  

 1 個參數不再多說,看前面的例子。  

 2 個參數是 sql 語句,跟 sqlite3_exec 裏的 sql 是一樣的。是一個很普通的以 \0 結尾的 char * 字符串。  

 3 個參數是查詢結果,它依然一維數組(不要以爲是二維數組, 更不要以爲是三維數組)。它內存佈局是:第一行是字段名稱,後面是緊接着是每個字段的值。下面用例子來說事。  

 4 個參數是查詢出多少條記錄(即查出多少行)。  

 5 個參數是多少個字段(多少列)。  

 6 個參數是錯誤信息,跟前面一樣,這裏不多說了。  

下面給個簡單例子 :  

int main( int , char ** )  

{  

   sqlite3 * db;  

   int result;  

   char * errmsg = NULL;  

   char **dbResult; // 是 char ** 類型,兩個* 號  

   int nRow, nColumn;  

   int i , j;  

   int index;  

   

   result = sqlite3_open ( “ c:\\Dcg_database.db ” , &db );  

   if( result != SQLITE_OK )  

   {  

        // 數據庫打開失敗  

        return -1;  

   }  

   

   // 數據庫操作代碼  

   // 假設前面已經創建了 MyTable_1 表  

   // 開始查詢,傳入的 dbResult 已經是 char ** ,這裏又加了一個 & 取地址符,傳遞進去的就成了 char ***  

   result = sqlite3_get_table ( db, “ select * from MyTable_1 ” , &dbResult, &nRow, &nColumn, &errmsg );  

   if( SQLITE_OK == result )  

   {  

        // 查詢成功  

        index = nColumn; // 前面說過 dbResult 前面第一行數據是字段名稱,從 nColumn 索引開始纔是真正的數據  

        printf( “ 查到%d 條記錄\n ” , nRow );  

   

        for(  i = 0; i < nRow ; i++ )  

        {  

             printf( “ 第 %d 條記錄\n ” , i+1 );  

             for( j = 0 ; j < nColumn; j++ )  

             {  

                  printf( “ 字段名:%s  ß > 字段值:%s\n ” ,  dbResult[j], dbResult [index] );  

                  ++index; // dbResult 的字段值是連續的,從 第0 索引到第 nColumn - 1 索引都是字段名稱,從第 nColumn 索引開始,後面都是字段值,它把一個二維的表(傳統的行列表示法)用一個扁平的形式來表示  

             }  

             printf( “ -------\n ” );  

        }  

   }  

   

   // 到這裏,不論數據庫查 詢是否成功,都釋放 char** 查詢結果,使用 sqlite 提供的功能來釋放  

   sqlite3_free_table ( dbResult );  

   

   // 關閉數據庫  

   sqlite3_close ( db );  

   return 0;  

}  

   

到這個例子爲止, sqlite3 的常用用法都介紹完了。  

用以上的方法,再配上 sql 語句,完全可以應付絕大多數數據庫需求。  

但有一種情況,用上面方法是無法實現的:需要 insert  select 二進制。當需要處理二進制數據時,上面的方法就沒辦法做 到。  

 

3 操作二進制  


sqlite 操作二進制數據需要用一個輔助的數據類型: sqlite3_stmt *   

這個數據類型記錄了一個“ sql 語句”。爲什麼我把  sql 語句” 用雙引號引起來?因爲你可以把 sqlite3_stmt * 所表示的內容看成是 sql 語句,但是實際上它不是我們所熟知的 sql 語句。它是一個已經把 sql 語句解析了的、用 sqlite 自己標記記錄的內部數據結構。  

正因爲這個結構已經被解析了,所以你可以往這個語句裏插入二進制數據。 當然,把二進制數據插到 sqlite3_stmt 結構裏可不能直接 memcpy ,也不能像 std::string 那樣用 號。必須用 sqlite 提供的函數來插入。  

   

 1 )寫入 二進制  

下面說寫二進制的步驟。  

要插入二進制,前提是這個表的字段的類型是 blob 類型。我假設有這麼一張表:  

create table Tbl_2( ID integer, file_content  blob )  

首先聲明  

sqlite3_stmt * stat;  

然後 ,把一個 sql 語句解析到 stat 結構裏去:  

sqlite3_prepare ( db, “ insert into Tbl_2( ID, file_content) values( 10, ? ) ” , -1, &stat, 0 );  

 面的函數完成 sql 語句的解析。第一個參數跟前面一樣,是個 sqlite3 * 類型變量,第二個參數是一個 sql 語句。  

這個 sql 語句特別之處在於 values 裏面有個 號。在 sqlite3_prepare 函數裏, ? 號表示一個未定的值,它的值等下才插入。  

第三個參數我寫的是 -1 ,這個參數含義是前面 sql 語句的長度。如果小於 0  sqlite 會自動計算它的長度(把 sql 語句當成以 \0 結尾的字符串)。  

第四個參數是 sqlite3_stmt 的指針的指針。解析以後的 sql 語句就放在這個結構裏。  

第五個參數我也不知道是幹什麼的。爲 0 就可以了。  

如果這個函數執行成功(返回值是 SQLITE_OK  stat 不爲 NULL ),那麼下面就可以開始插入二進制數據 。  

sqlite3_bind_blob ( stat, 1, pdata, ( int )(length_of_data_in_bytes), NULL ); // pdata 爲數據緩衝區,length_of_data_in_bytes 爲數據大小,以字節爲單位  

這個函數一共有 5 個參數。  

 1 個參數:是前面 prepare 得到的 sqlite3_stmt * 類型變量。  

 2 個參數: ? 號的索引。前面 prepare  sql 語句裏有一個 ? 號,假如有多個 ? 號怎麼插入?方法就是改變 bind_blob 函數第 2 個參數。這個參數我寫 1 ,表示這裏插入的值要替換 stat 的第一個 ? 號(這裏的索引從 1 開始計數,而非從 0 開始)。如果你有多個 ? 號,就寫多個 bind_blob 語句,並改變它們的第 2 個參數就替換到不同的 ? 號。如果有 ? 號沒有替換, sqlite 爲它取值 null   

 3 個參數:二進制數據起始指針。  

 4 個參數:二進制數據的長度,以字節爲單位。  

 5 個參數:是個析夠回調函數,告訴 sqlite 當把數據處理完後調用此函數來析夠你的數據。這個參數我還 沒有使用過,因此理解也不深刻。但是一般都填 NULL ,需要釋放的內存自己用代碼來釋放。  

bind 完了之後,二進制數據就進入了你的“ sql 語句”裏了。你現在可以把它保存到數據庫裏:  

int result = sqlite3_step ( stat );  

通過這個語 句, stat 表示的 sql 語句就被寫到了數據庫裏。  

最後,要把 sqlite3_stmt  構給釋放:  

sqlite3_finalize ( stat ); // 把剛纔分配的內容析構 掉  

   

 2 )讀出 二進制  

下面說讀二進制的步驟   

跟前面一樣 ,先聲明 sqlite3_stmt * 類型變量:  

sqlite3_stmt * stat;  

然後, 把一個 sql 語句解析到 stat 結構裏去:  

sqlite3_prepare ( db, “ select * from Tbl_2 ” , -1, &stat, 0 );  

當 prepare 成功之後(返回值是 SQLITE_OK ),開始查詢數據。  

int result = sqlite3_step ( stat );  

這一句的返回值是 SQLITE_ROW 時表示成功(不是 SQLITE_OK )。  

你可以循環執行 sqlite3_step 函數,一次step 查詢出一條記錄。直到返回值不爲 SQLITE_ROW 時表示查詢結束。  

然後開始獲取第一個字段 :ID 的值。ID 是個整數,用下面這個語句獲取它的值:  

int id = sqlite3_column_int( stat, 0 ); // 第2 個參數表示獲取第幾個字段內容,從0 開始計算,因爲我的表的ID 字段是第一個字段,因此這裏我填0  

   

下面開始獲取 file_content 的值,因爲 file_content 是二進制,因此我需要得到它的指針,還有它的長度:  

const void * pFileContent = sqlite3_column_blob ( stat, 1 );  

int len = sqlite3_column_bytes ( stat, 1 );  

這樣就得到了二進制的值 。  

把 pFileContent 的內容保存出來之後,不要忘了釋放 sqlite3_stmt 結構:  

sqlite3_finalize ( stat ); // 把剛纔分配的內容析構 掉  

   

 3 )重複 使用 sqlite3_stmt 結構  

如果你需要重複使用 sqlite3_prepare 解析好的 sqlite3_stmt 結構,需要用函數: sqlite3_reset 。  

result = sqlite3_reset (stat);  

這樣, stat 結構又成爲 sqlite3_prepare 完成時的狀態,你可以重新爲它 bind 內容。  


4 事務處理  


sqlite 是支持事務處理的。如果你知道你要同步刪除很多數據,不仿 把它們做成一個統一的事務。  

通常一  sqlite3_exec 就是一次事務,如果你要刪除 1 萬條數據, sqlite 就做了 1 萬次:開始新事務 -> 刪除一條數據 -> 提交事務 -> 開始新事務 ->  的過程。這個操作是很慢的。因爲時間都花在了開始事務、提 交事務上。  

你可以把這些同類操作做成一個事務,這樣如果操作錯誤,還能夠回滾事 務。  

事務的操作沒有特別的接口函數,它就是一個普通的 sql 語句而已:  

分別如下 :  

int result;   

result = sqlite3_exec ( db, "begin transaction", 0, 0, &zErrorMsg ); // 開始一個事務  

result = sqlite3_exec ( db, "commit transaction", 0, 0, &zErrorMsg ); // 提交事務  

result = sqlite3_exec ( db, "rollback transaction", 0, 0, &zErrorMsg ); // 回滾事務  

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章