sqlite3(官方文檔)是一種輕量級且應用普遍的關係型數據庫的一種。sqlite3數據庫可運行在類Unix和window系統之上,其中,大部分的類Unix系統(如Linux)都默認安裝了sqlite3數據庫,我的使用的系統爲Ubuntu 16.04,默認狀況下系統已經安裝了sqlite3數據庫,檢測方法爲:在Terminal中執行sqlite3
,便可進入sqlite3的命令編輯模式,若失敗請手動編譯安裝sqlite3數據庫。html
首先明白幾個概念:ios
1.DML(Data Manipulattion Language),顧名思義--數據操做語言。所謂數據操做,是指對數據的插入(insert),刪除(delete),更新(update)等。 2.DQL(Data Query Language)--數據查詢語言。DQL的基本結構是由SELECT等查詢語句及其子語句組成。 3.DDL(Data Definition Language)--數據定義語言。DDL是對數據庫進行建立的語言。好比建立數據庫(create database),建立數據表(create table),刪除數據表(drop table)等。
sqlite有其獨特的語法規則。c++
1.大小寫的敏感性.在sqlite中,是不區分大小寫的,例如建立表語句`CREATE TABLE tablename;`;和`create table tablename;`效果是徹底同樣的,再好比查詢語句'select * from tablename'和'SELECT * FROM tablename'也是徹底同樣的。 2.全部的sqlite語句能夠以任何關鍵字開始,`select`,`insert`,`create`等,以距離最近的 **「;」** 做爲本條語句的結束標誌。
sqlite數據庫中有一些特殊的命令,這些特殊的命令都以 .(點)
開頭,命令結尾不須要以 「;」 結尾,這些特殊的命令被稱爲sqlite的點命令。有一個特殊的點命令.help
,它能夠列出全部sqlite的點命令及其使用說明,如圖所示:sql
sqlite提供原生態的C/C++接口,從其C/C++接口文檔能夠查看全部的接口及使用方法。簡單介紹幾個經常使用的對象和接口:編程
1.`sqliet3`.數據庫對象,相似於數據庫的入口。 2.`sqlite3_stmt`.狀態存儲對象,按照官網解釋 「 An instance of this object represents a single SQL statement that has been compiled into binary form and is ready to be evaluated.」 通俗來講,就是它是一個對象實體,該實體所存儲的東西是已經**預編譯**好的單條SQL語句,這個實體對象能夠被後續的sqlite3語句執行。`sqlite3_stmt`對象相似於C/C++語言中**預編譯**的結果(在C++編程中,預編譯的結果存儲在二進制文件中,咱們通常看不到)。 3.`sqlite3_open(const char *filename,sqlite3 **ppDb)`接口。該函數用於打開或建立(若`filename`不存在)一個數據庫對象,建立的結果保存在`ppDd`所指定的`sqlite3`對象中. 4.`sqlite3_prepare_v2(sqlite3 *db,const char *zSql,int nByte,sqlite3_stmt **ppStmt,const char **pzTail);`接口,該函數主要是對SQL語句進行預編譯,並將結果存在參數4指定的對象中。其中參數2`zSal`是代指一條SQL語句的字符串,參數3代指該`zSql`字符串的最大佔用字節長度,參數4 `雙重指針參數ppStmt`即存儲通過sqlite3編譯後的SQL語句的結果,最後一個參數指代參數`zSql`字符串的沒有使用的地址偏移量,通常置`nullptr`便可。該函數執行成功,返回`int`類型的`SQLITE_OK`,失敗返回錯誤代碼。 5.`sqlite3_step(sqlite3_stmt *);`接口,該函數主要是用於執行`sqlite3_prepare()`函數所編譯好的`sqlite3_stmt`對象。對與DDL和DML而言,該函數執行成功返回`SQLITE_DONE`,對於DQL而言,若執行成功,則返回`SQLITE_ROW`。 6.`sqlite3_finalize(sqlite3_stmt *pStmt);`接口,主要用於釋放`sqlite3_stmt`對象所佔用的資源,執行成功過返回`SQLITE_OK;`。 7.`sqlite3_close(sqlite3 *);`接口,該函數主要用於關閉`sqlite3`數據庫對象,成功返回`SQLITE_OK`。
以上兩個對象(1 和 2)以及5個接口函數(3-7)應該算是SQLite c/c++編程中最重要也是最基礎的,幾乎每一個SQLite C編程都離不開。固然還有一些其餘重要的函數,好比sqlite3_bind()
族類,sqlite3_column()
族類,以及sqlite3_exec()
等,這些函數的存在極大方便了sqlite3
編程。函數
在這裏建立了一個sqlite操做的類Data
,全部對sqlite數據庫的操做都做爲該類的方法,該程序文件以下:測試
#include <sqlite3.h> #include <string.h> #include <iostream> #include <string> using namespace std; //建立操做數據庫的類 class Data { public: Data(){ cout<<"執行構造函數"<<endl; }; ~Data(){ cout<<"執行析構函數"<<endl; } //1.建立數據庫和數據表 void createData() ; //2.插入數據 void insertData(); //3.更新數據(更新第id條記錄的第n列字段的值) void updateData(const int id,const int n ); //4.顯示開啓事物,批量插入數據 void insertBatch() ; //5.選擇數據(選擇特定行數的數據並打印出來) void selectData(const int row,const int offset) ; //6.關閉數據庫 void close(){ sqlite3_close(conn); return ; } //7.清空數據庫表,以便於下次操做測試 void drop(); private: sqlite3 *conn = nullptr; int rc; int count = 10; }; void Data::updateData(const int id,const int n){ const char *field = nullptr; const char *update ; char str[64] = {0}; if(1 == n){ cout<<"約束主鍵不能修改"<<endl; // field = "ID"; // update = "update test set ID = %d where ID = %d"; // sprintf(str,update,n,id); }else if(2 == n){ field = "name"; update = "update test set name = '%s' where ID = %d"; sprintf(str,update,"yxg",id); }else{ field = "width"; update = "update test set width = %f where ID = %d"; sprintf(str,update,99.8,id); }; sqlite3_stmt *stmt = nullptr; if(sqlite3_prepare_v2(conn,str,strlen(str),&stmt,nullptr) != SQLITE_OK){ sqlite3_finalize(stmt); close(); return ; } if(sqlite3_step(stmt)!=SQLITE_DONE){ sqlite3_finalize(stmt); close(); return; } sqlite3_finalize(stmt); } void Data::selectData(const int row,const int offset) { int r = row,o = offset; const char * select1 = "select * from test limit %d,%d "; char select[64] = {0}; sprintf(select,select1,row,offset); sqlite3_stmt *stmt = nullptr; // const char * select = "select * from test limit 1,1"; if(sqlite3_prepare_v2(conn,select,strlen(select),&stmt,nullptr) != SQLITE_OK){ cout<<"編譯select語句失敗"<<endl; close(); sqlite3_finalize(stmt); return ; } if(sqlite3_step(stmt) == SQLITE_ROW){ // cout<<"查詢好了:"<<endl; int fieldcount = sqlite3_column_count(stmt); cout<<"該表所含字段數量是:"<<fieldcount<<endl; for(int i = 0;i < fieldcount; ++i){ int type = sqlite3_column_type(stmt,i); if(type == SQLITE_INTEGER){ int v = sqlite3_column_int(stmt,i); cout<<"ID is: "<<v<<endl; }else if(type == SQLITE_TEXT){ const char *v=(const char *)sqlite3_column_text(stmt,i); string s = v; cout<<"Name is: "<<s<<endl; }else if(type == SQLITE_FLOAT){ int v = sqlite3_column_int(stmt,i); cout<<"Age is: "<<v<<endl; }else{ cout<<"The result is nullptr!!"<<endl; } } } sqlite3_finalize(stmt); } void Data::drop(){ const char *drop = "drop table test"; sqlite3_stmt *stmt = nullptr; if(sqlite3_prepare_v2(conn,drop,strlen(drop),&stmt,nullptr ) != SQLITE_OK){ close(); sqlite3_finalize(stmt); return ; } if(sqlite3_step(stmt) == SQLITE_DONE){ cout<<"成功銷燬數據表"<<endl; } sqlite3_finalize(stmt); close(); } void Data::insertBatch(){ //開啓一個事物 const char *begin = "begin transaction"; sqlite3_stmt *stmt = nullptr; if(sqlite3_prepare_v2(conn,begin,strlen(begin),&stmt,nullptr)!=SQLITE_OK){ close(); cout<<"預編譯事物失敗!!"<<endl; return ; } if(sqlite3_step(stmt) != SQLITE_DONE){ close(); cout<<"執行事物失敗"<<endl; return; } sqlite3_finalize(stmt); //基於綁定變量插入數據 const char *insert = "insert into test values(?,?,?)"; sqlite3_stmt *stmt2 = nullptr; if(sqlite3_prepare_v2(conn,insert,strlen(insert),&stmt2,nullptr) != SQLITE_OK){ close(); return ; } // char *name = "this is name"; for(int i = 0; i < count; ++i){ //數據表最左邊的索引爲1 sqlite3_bind_int(stmt2,1,i); string name = "this is name "; name += to_string(i); sqlite3_bind_text(stmt2,2,name.c_str(),sizeof(name),SQLITE_TRANSIENT); sqlite3_bind_double(stmt2,3,19.1*i); if(sqlite3_step(stmt2)!= SQLITE_DONE){ close(); sqlite3_finalize(stmt2); return ; } sqlite3_reset(stmt2); cout<<"Insert succeed!"<<endl; } sqlite3_finalize(stmt2); //提交事務 const char * commit= "commit"; sqlite3_stmt *stmt3 = nullptr; if(sqlite3_prepare_v2(conn,commit,strlen(commit),&stmt3,nullptr)!= SQLITE_OK){ close(); sqlite3_finalize(stmt3); return; } if(sqlite3_step(stmt3)!=SQLITE_DONE){ close(); sqlite3_finalize(stmt3); return; } sqlite3_finalize(stmt3); } void Data::createData() { //在當前目錄下打開(或建立)test.db數據庫。 rc = sqlite3_open("test.db",&conn); if(rc != SQLITE_OK){ close(); cout<<"建立數據庫失敗!!"<<endl; return ; } //SQL語句 /* const char *createTable = "create table test(" \ "ID INT PRIMARY KEY NOT NULL,"\ "name TEXT NOT NULL," \ "age INT NOT NULL," \ " );";*/ const char * createTable = "create table test(ID INT PRIMARY KEY NOT NULL,name TEXT,width REAL)"; sqlite3_stmt *stmt = nullptr; //預編譯SQL語句 if(sqlite3_prepare_v2(conn,createTable,strlen(createTable),&stmt,nullptr) != SQLITE_OK){ cout<<"預編譯失敗"<<endl; sqlite3_finalize(stmt); close(); return; } //執行SQL語句 if(sqlite3_step(stmt) != SQLITE_DONE){ sqlite3_finalize(stmt); cout<<"執行失敗"<<endl; close(); return ; } sqlite3_finalize(stmt); cout<<"建立數據庫和數據表成功!!"<<endl; } int main(int argc, char **argv){ Data *base = new Data; base->createData(); base->insertBatch(); //打印從第一條記錄開始的一條記錄(記錄索引從0開始) base->selectData(1,1); base->updateData(1,2); base->drop(); delete base; base = nullptr; return 0; }
從以上簡單示例能夠看出,sqlite3
c++編程基本都要通過三個步驟(在不使用sqlite3_exec()
):優化
1.聲明`SQL`語句和`sqlite3_stmt` 對象; 2.調用`sqlite3_prepare()`函數 「預編譯」; 3.調用`sqlite3_step()`函數執行`SQL`操做.
在上面幾個類方法中,惟一有點特別的是void insertBatch()
函數,與其餘功能函數的主要區別就是:在該函數中,顯示的開啓了一個事務,全部的插入操做均在該事務內完成,直到該事務被顯示的提交。
所謂事務,其實就是一塊執行單元,一塊代碼段,在該執行單元內的sqlite操做,都不會自動的提交到數據庫中,直到顯示的執行 SQL commit
。在sqlite中,全部的操做本質都是「事務性」的,只不過被sqlite隱式的建立並提交,默認狀況下,每個sqlite3
函數的調用都伴隨着事務的發生。
通常狀況下,數據插入相似下面的代碼:this
void insert(){ const char *insertSQL= " ..... "; sqlite3_stmt 對象; for(int i=0;i < count;++i){ 執行預編譯 sqlite3_prepare(); 執行插入 sqlite3_step(); .... .... } }
在方法void insertBatch()
中,如要插入大批量數據,只須要執行一次 「預編譯」 便可,而後基於數據綁定的方式在事務內部執行SQL插入語句,極大節省了sqlite3_prepare_v2()
函數的調用次數(一般 函數sqlite3_prepare_v2()
比函數sqlite3_step()
執行時佔用時間更多)。
能夠看出,在執行大批量操做時,顯示的建立並提交事務,可以優化編碼,並提升代碼執行效率,而且各個事務之間是隔離
狀態--不一樣事務之間相互獨立和透明的。
注意:在結束數據庫操做的時候,必定記得執行sqlite3_close()
函數關閉該庫,在執行關閉操做以前,也要確保每個sqlite3_stmt
對象的資源被釋放(調用sqlite3_finalize()
),不然容易形成內存泄露。